强制重排重绘应避开直接读取offsetTop等后立即改样式,需合并读取、延后布局读取时机,用transform/opacity替代高代价属性,虚拟滚动优化大量DOM节点。

强制重排重绘的 JavaScript 操作要避开
直接读取 offsetTop、clientWidth、getComputedStyle() 后立刻修改样式,会触发同步布局计算(Layout),在动画帧中尤其致命。浏览器不得不中断渲染流水线,回溯计算几何信息,造成卡顿。
- 把多次读取合并成一次,例如先缓存
element.getBoundingClientRect()结果再取top和width - 避免在
requestAnimationFrame回调开头就读取布局信息;应放在回调末尾,或用setTimeout(() => {}, 0)推迟到下一帧(仅限非动画场景) - 用
transform和opacity替代top/left/width等触发布局的属性做动画
CSS 中哪些属性会触发 Layout / Paint / Composite
不是所有样式变更代价相同。现代浏览器将渲染流水线分为 Layout(计算位置大小)、Paint(填充像素)、Composite(图层合成)。关键在于让动画只走 Composite 阶段。
- 安全属性(只触发 Composite):
transform(含translate3d)、opacity - 高代价属性(触发 Layout + Paint):
height、width、margin、padding、border、font-size -
will-change: transform可提前提示浏览器升层,但别滥用——长期开启会占用额外内存和 GPU 资源
大量 DOM 节点导致渲染慢怎么办
当列表项超过几百条,哪怕只是 display: none,DOM 树深度和节点数仍会拖慢样式计算与布局。虚拟滚动(virtual scrolling)不是“可选优化”,而是必要手段。
- 用
IntersectionObserver替代scroll事件监听,避免频繁触发重排 - 只渲染视口内 ±1–2 屏的内容,其余用占位
div保持滚动高度不变 - 避免在
innerHTML中拼接长 HTML 字符串;改用documentFragment或createElement批量插入
const fragment = document.createDocumentFragment();
for (let i = 0; i < visibleItems.length; i++) {
const el = document.createElement('li');
el.textContent = visibleItems[i];
fragment.appendChild(el);
}
listElement.appendChild(fragment);
Canvas 2D 绘制卡顿的常见误操作
HTML5 页面里用 做图表、游戏或粒子效果时,性能瓶颈往往不在 GPU,而在 CPU 的 JS 执行和 Canvas API 调用开销。
立即学习“前端免费学习笔记(深入)”;
- 不要每帧都调用
ctx.clearRect(0, 0, width, height)—— 改用ctx.save()/ctx.restore()或局部擦除 - 避免在绘制循环中反复创建路径(
beginPath()→moveTo()→ ...);预生成Path2D对象复用 - 启用
will-read-frequently属性(const canvas = document.createElement('canvas', { 'will-read-frequently': true }))可提升getImageData性能,但会禁用硬件加速,需权衡
offsetHeight 读取混在滚动节流函数里,或者给 2000 个 div 同时加 transition: all。定位得靠 Chrome DevTools 的 Rendering 面板 + FPS meter,而不是猜。











