应避免在列表项上使用will-change,而只对滚动容器设will-change:transform;禁用scroll中实时读取scrolltop;优先采用虚拟滚动方案。

will-change 用在列表项上反而让滚动更卡
直接给每个 li 或 .item 加 will-change: transform 是常见误区。浏览器会为每个元素单独创建合成层,内存暴涨,GPU纹理上传压力大,尤其在中低端安卓机上,帧率可能从 60fps 掉到 20fps。
- 只对「真正需要频繁重绘的容器」设
will-change,比如包裹整个列表的.list-container - 避免在滚动过程中动态增删
will-change—— 切换本身触发样式重计算,比不加还慢 - Chrome DevTools 的「Layers」面板能直观看到合成层数量,超过 5~8 层就要警惕
移动端 scroll 事件里读取 scrollTop 就是性能杀手
哪怕只是写一行 console.log(container.scrollTop),也会强制触发同步布局(layout thrashing):浏览器必须立刻回滚渲染流水线、重新计算所有元素位置,再把结果吐给你。长列表一滚动,就掉帧。
- 改用
getBoundingClientRect()或element.offsetTop预先缓存位置,避免实时读取滚动偏移 - 真要响应滚动位置,用
requestAnimationFrame节流 +passive: true注册事件:container.addEventListener('scroll', handler, { passive: true }) - iOS Safari 对
scroll事件监听更敏感,没加passive可能直接禁用惯性滚动
虚拟滚动比 will-change 更治本,但别自己手写
will-change 是修水管,虚拟滚动是换管道。它只渲染视口内 10~20 个节点,其余用 padding-top/bottom 占位,DOM 节点数从几千降到个位数,滚动自然顺滑。
- 优先用成熟库:
react-window(React)、vue-virtual-scroller(Vue),它们已处理好 iOS 滚动边界、键盘聚焦、可访问性等坑 - 别用
position: sticky或transform: translateY()模拟滚动位移 —— 这些仍需浏览器持续计算每个元素位置 - 服务端返回数据若带分页游标(cursor),就别传 total count,虚拟滚动不需要知道总条数
will-change 必须配合 transform 才生效
只写 will-change: opacity 或 will-change: left 在现代浏览器里基本无效。浏览器只对能进合成层的属性做预优化,而 opacity 和 transform 是唯二被广泛支持的合成属性。
- 正确写法:
will-change: transform,且对应元素必须有实际transform变化(哪怕只是transform: translateZ(0)) -
left/top触发的是 layout → paint → composite 流程,永远进不了合成层,will-change对它没意义 - 加了
will-change却没后续动画,浏览器会在约 200ms 后自动降级,但期间已占用资源
will-change 用没用对,而在 DOM 节点是否该存在。先砍掉 90% 的节点,再考虑要不要给剩下那几个加 transform 和 will-change。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











