Skip to content

重绘与回流

重绘(Repaint)和回流(Reflow)是浏览器渲染页面的两个关键步骤,频繁触发会导致页面卡顿。

1. 回流 Reflow

1.1 什么是回流

  • 当元素的几何属性(尺寸、位置)发生变化时,浏览器需要重新计算布局。
  • 回流开销大,可能触发整个文档或大量元素的重新布局。

1.2 触发回流的常见操作

  • 修改 widthheightmarginpaddingborder
  • 修改 displaypositiontopleft
  • 读取 offsetWidthoffsetHeightclientWidthscrollTop 等。
  • 浏览器窗口大小变化、字体变化。
js
// 会触发回流
box.style.width = '100px';
box.style.height = '100px';

2. 重绘 Repaint

2.1 什么是重绘

  • 元素的外观发生变化,但几何属性不变。
  • 浏览器只需重新绘制该元素。

2.2 触发重绘的常见操作

  • 修改 colorbackground-colorbox-shadowborder-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

  • transformopacity 可以触发 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。