虚拟滚动生效的前提是删除95%以上dom节点,滚动容器须设固定高度和overflow-y: auto,占位高度按totalitems×itemheight计算,渲染区用position: absolute或transform: translatey(),监听需节流+passive:true,索引用二分查找,非数据节点需纳入数组统一管理。

直接删掉 95% 以上的 DOM 节点,不是“隐藏”,而是根本不去创建——这是虚拟滚动能起效的唯一前提。
滚动容器必须设固定高度 + overflow-y: auto
浏览器只有在明确滚动上下文中,才能提供稳定、可预测的 scrollTop 值。如果容器是 height: auto、min-height,或嵌套在 flex / grid 自适应父容器里,scrollTop 会跳变甚至归零,整个索引计算就失效。
- 正确写法:
height: 600px; overflow-y: auto; - 该元素不能被父级
overflow: hidden裁剪,否则滚动事件不触发 - Vue 中若用
v-virtual-scroll,需确认插件文档是否要求 wrapper 显式设高 - React 中用
ref绑定时,必须指向这个带overflow的容器,不是外层 wrapper
占位高度必须用 totalItems × itemHeight 计算
撑出滚动条靠的是一个无内容、纯高度的占位元素(phantom),它的高度决定了滚动范围。一旦算错,滚动条长度失准,用户拖动距离和实际位移就不匹配。
- 等高场景:直接
totalHeight = data.length * itemHeight - 变高场景不要硬上——预估误差超 ±15px 就会明显“跳行”,优先考虑服务端预存高度字段或前端分组折叠
- 表格场景加
table-layout: fixed+ 显式列宽,防止内容撑开导致行高浮动 - 别用
display: none或visibility: hidden替代占位——它们仍驻留 DOM 树,内存和样式计算开销照旧
真实渲染区必须用 position: absolute 或 transform: translateY()
二者选其一,但不能混用,且各有陷阱:
-
position: absolute更适合编辑器类场景:top 值基于行索引线性计算,selection API 坐标系稳定,光标不丢 -
transform: translateY()在 Chrome 115–122 中可能意外触发额外合成层,GPU 内存暴涨;但它不改变文档流,适合纯展示列表 - 无论哪种,都禁用
margin-top累加——每帧重排整个容器,性能差一个数量级 - 容器本身加
contain: layout paint,告诉浏览器“不可见区域不用绘制”
滚动监听必须节流 + passive: true
原生 scroll 事件在 Chrome/Firefox 中每秒可触发 60–120 次,不加限制等于每秒执行几十次 DOM 查询 + 计算 + 渲染。
- 必须用
requestAnimationFrame节流,避免帧率暴跌 - 监听时加
{ passive: true },否则 Chrome 强制同步执行 handler,拖垮主线程 - 起始索引必须用二分查找(如 Lodash
sortedIndex或手写带边界检查函数),不能用findIndex——后者是 O(n),10 万条数据滚动即卡死 - 非数据节点(广告、分组标题)必须作为数据项塞进数组,用
type字段区分,否则索引错位、translateY 偏差、滚动跳变
最常被忽略的不是算法,而是 DOM 生命周期管理:节点池复用、replaceChildren() 清空、key 稳定性、以及 contenteditable 场景下对 selection 和 focus 的显式维护——这些细节决定虚拟滚动是“流畅”还是“看似流畅实则随时崩”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











