虚拟列表通过只渲染可视区域及缓冲区的dom节点、用占位元素维持滚动条高度、以transform定位内容来避免重排,从根本上解决大规模dom渲染卡顿。

解决大规模 DOM 节点渲染卡顿,虚拟列表不是“让页面变快一点”的技巧,而是从根源上避免浏览器处理不该处理的节点。它的核心思路就一条:不渲染看不见的项,只维护当前可视区域加少量缓冲的 DOM。
只渲染“看得见”的那几十个
用户滚动时,真正进入视野的永远只是固定高度的一小段区域。比如容器高 600px、每行高 60px,最多同时看到 10 行——虚拟列表就只创建这 10 个 DOM 元素,而不是十万条全挂上去。
- 通过
scrollTop和itemHeight算出当前应显示的起始索引:startIndex = Math.floor(scrollTop / itemHeight) - 再结合容器高度算出可视行数,得出结束索引:
endIndex = startIndex + Math.ceil(containerHeight / itemHeight) + buffer - 实际渲染的数据就是
list.slice(startIndex, endIndex),数量恒定,与总数据量无关
用占位元素撑开滚动条
如果只渲染可见部分,滚动条会消失或错位。所以必须保留“整体高度感”:
- 插入一个空的
<div class="phantom">,高度设为 <code>total * itemHeight - 它不包含任何内容,也不参与交互,纯粹用来告诉浏览器:“这个列表本该这么长”
- 滚动条位置、拖拽距离、惯性滚动等原生行为全部正常,用户毫无感知
- 把真实内容包裹在
position: relative容器里 - 用
transform: translateY(offset)把内容整体下拉,偏移量offset = startIndex * itemHeight - 这样内容始终“对齐”滚动位置,但不触发布局计算,性能开销极低
- 上下各多渲染 5–10 条(具体看业务节奏,通常
buffer = 5足够) - 缓冲区内容随滚动动态更新,始终包围着可视区域
- 太小:快滑时露白;太大:DOM 数量回升,失去优化意义
靠 transform 定位真实内容
渲染区不能随滚动自然位移(否则会触发重排),必须用 CSS 变换实现“视觉跟随”:
缓冲区是流畅滚动的关键
纯可视区域渲染在快速滚动时容易闪白——因为新内容还没来得及渲染出来。缓冲区就是提前多画几行:










