虚拟列表,只渲染看得见的部分
长列表的性能问题,核心就一句话:DOM 元素太多了。一万条数据生成一万个 DOM 节点,浏览器光是维护这个 DOM 树就要消耗大量内存,滚动起来卡顿是必然的。
虚拟列表的思路很简单:不管数据有多少条,我只渲染用户当前能看到的那几项。
核心原理
虚拟列表基于一个事实:用户在一个固定高度的容器里滚动,任何时候能看到的条目数量是有限的。比如容器高 600px,每项高 100px,那用户最多只能看到 6 项。
所以不管数据是 100 条还是 10000 条,DOM 里始终只保留 6-8 个节点(加几个缓冲区防止闪烁)。
实现要点
1. 计算可见范围
javascript
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = startIndex + visibleCount;
const renderList = data.slice(startIndex, endIndex);2. 视觉欺骗
为了让滚动条显示正确的总长度,需要把列表容器的高度设置为 data.length * itemHeight。然后用 transform: translate3d(0, ${startIndex * itemHeight}px, 0) 把可见区域移到正确位置。
这样用户滚动时,滚动条的位置是对的,但实际渲染的只有那几项。
3. 滚动节流
滚动事件触发非常频繁,需要用 requestAnimationFrame 节流:
javascript
function rafThrottle(fn) {
let lock = false;
return function (...args) {
requestAnimationFrame(() => {
if (lock) return;
lock = true;
fn.apply(this, args);
lock = false;
});
};
}适用场景
虚拟列表适合固定高度的列表项。如果每项高度不固定,就需要额外的高度缓存和动态计算,复杂度会高很多。
我最早接触虚拟列表是在做停车业务的订单列表时,几千条订单数据直接渲染,页面卡得不行。改成虚拟列表后,DOM 节点从几千个降到了十几个,滚动流畅了很多。
有时候性能优化就是这样——不是做了什么复杂的事,而是想明白了"不需要做什么"。
