Elements面板中右键滚动容器选“Break on→subtree modifications”,若断点频繁触发,说明scroll回调反复增删DOM;document.querySelectorAll('.list-item').length超200需警觉,500+基本确认未用虚拟滚动。

长列表卡顿不是 JS 逻辑慢,而是浏览器被 DOM 和布局拖垮了——调试必须绕开 console.time,直击渲染流水线瓶颈。
Chrome DevTools 里怎么看是不是 DOM 爆炸
打开 Elements 面板,选中滚动容器(比如 div.list-container),右键 → “Break on” → “subtree modifications”,然后滚动。如果断点频繁触发,说明你在 scroll 回调里反复增删 DOM(比如用 innerHTML = '' 再拼接);更隐蔽的是 Vue/React 中 key 错乱导致节点不断重建。直接看 document.querySelectorAll('.list-item').length:超过 200 就该警觉,500+ 基本确定是全量渲染没走虚拟滚动。
滚动时强制同步布局(Layout Thrashing)怎么定位
在 Performance 面板录制一次滚动操作,展开“Main”线程,找黄色的 Layout 块——只要它密集出现且单次耗时 >8ms,基本就是 getBoundingClientRect()、offsetHeight 或 clientWidth 在 scroll 回调里被调用了。特别注意:哪怕只写了一行 console.log(el.offsetHeight),也会触发整页重排。验证方法:把所有 layout 读取代码注释掉,再测帧率;若卡顿消失,问题就在这里。
虚拟滚动是否生效,看这三处
不用猜,直接查:
立即学习“前端免费学习笔记(深入)”;
- 滚动容器是否有明确
height或max-height?height: auto或靠flex: 1撑高会导致计算失效 - 占位层(phantom)高度是否精确等于
itemCount * itemSize?差 1px 就会导致滚动条缩放错乱、scrollTop映射偏移 - 真实内容区是否用
transform: translateY()或top定位?用margin-top或padding-top会触发重排,且无法精准对齐
为什么加了 -webkit-overflow-scrolling: touch 还是卡
单独加这一条没用,必须配对:
-
-webkit-overflow-scrolling: touch(iOS 启用惯性滚动) -
transform: translateZ(0)(安卓 WebView 和部分 Chrome 强制创建合成层)
缺一不可。验证方式:DevTools → Rendering → 勾选 “Layer Borders”,滚动时容器边缘应有绿色边框;没有,说明合成层没建起来。另外检查父级有没有 filter、opacity: 0.99 或 mask,这些会干扰图层分离。
真正难的不是让虚拟滚动跑起来,而是确保每次滚动只改 transform 和文本内容,其余任何 DOM 结构变动、样式重算、事件重绑,都会在快速滚动中立刻暴露——别信“差不多”,DOM 复用必须靠 dataset.id 查 Map,不能靠数组索引。



















