重绘与回流
重绘(Repaint)和回流(Reflow)是浏览器渲染页面的两个关键步骤,频繁触发会导致页面卡顿。
1. 回流 Reflow
1.1 什么是回流
- 当元素的几何属性(尺寸、位置)发生变化时,浏览器需要重新计算布局。
- 回流开销大,可能触发整个文档或大量元素的重新布局。
1.2 触发回流的常见操作
- 修改
width、height、margin、padding、border。 - 修改
display、position、top、left。 - 读取
offsetWidth、offsetHeight、clientWidth、scrollTop等。 - 浏览器窗口大小变化、字体变化。
js
// 会触发回流
box.style.width = '100px';
box.style.height = '100px';2. 重绘 Repaint
2.1 什么是重绘
- 元素的外观发生变化,但几何属性不变。
- 浏览器只需重新绘制该元素。
2.2 触发重绘的常见操作
- 修改
color、background-color、box-shadow、border-color。 - 修改
visibility(注意不是display)。 - 修改
outline。
3. 回流与重绘的关系
- 回流一定导致重绘。
- 重绘不一定会导致回流。
修改几何属性 → 回流 → 重绘
修改外观属性 → 重绘4. 优化策略
4.1 批量修改样式
js
// 不好:多次触发回流
box.style.width = '100px';
box.style.height = '100px';
box.style.margin = '10px';
// 好:合并为一次
box.style.cssText = 'width:100px;height:100px;margin:10px;';
// 或添加 class
box.classList.add('active');4.2 避免强制同步布局
js
// 不好:读-写-读-写交替
const w = box.offsetWidth; // 读
box.style.width = (w + 10) + 'px'; // 写
const h = box.offsetHeight; // 读(强制同步布局)
box.style.height = (h + 10) + 'px'; // 写
// 好:先读后写
const w = box.offsetWidth;
const h = box.offsetHeight;
box.style.width = (w + 10) + 'px';
box.style.height = (h + 10) + 'px';4.3 使用文档片段或离线 DOM
js
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = i;
fragment.appendChild(li);
}
list.appendChild(fragment); // 只触发一次回流4.4 使用 transform 和 opacity
transform和opacity可以触发 GPU 加速,不会引起回流。- 适合动画场景。
css
.box {
transform: translateX(100px);
opacity: 0.5;
}4.5 使用 will-change
css
.animated {
will-change: transform;
}注意:动画结束后应移除
will-change,避免占用 GPU 资源。
5. 性能检测
- Chrome DevTools Performance 面板查看 Rendering 事件。
- 关注 Long Task 和 Layout Shift。