Day 01 - 重排(Reflow)与重绘(Repaint)

352 阅读3分钟

🗓️ 目标:100 篇
✅ 已完成:0 篇
📍 当前:Day 01 - 重排(Reflow)与重绘(Repaint)

本篇深入聊聊前端性能优化中的 重排(Reflow)与重绘(Repaint),这是每个前端工程师都应该掌握的底层知识之一。

🔍 一、为什么了解重排和重绘很重要?

浏览器是一个复杂的渲染引擎,它需要不断地把 HTML、CSS 和 JS 合成出一个用户可以交互的界面。我们写的代码会触发浏览器执行一系列渲染步骤:

HTML 解析 → DOM 构建
CSS 解析 → CSSOM 构建
DOM + CSSOM → 渲染树(Render Tree)
渲染树布局(Layout/Reflow)
像素绘制(Paint/Repaint)
合成图层 → 显示

其中,Reflow 和 Repaint 是最容易被忽略却最影响性能的两个步骤。

🧱 二、什么是重排(Reflow)?

定义: 当页面的结构或元素的位置、大小发生变化时,浏览器会重新计算元素的位置和几何信息,这就是 重排

✅ 会触发重排的操作有:

  • 添加、删除 DOM 节点

  • 改变元素的尺寸(widthheightpaddingmarginborder

  • 改变元素的显示状态(如 display: noneblock

  • 改变页面布局的属性(如 position, top, left, flex, grid 等)

  • 读取布局属性(触发强制同步):

    • offsetWidth, offsetHeight, clientTop, scrollTop, getBoundingClientRect() 等等

💡 一些方法读取布局信息时会强制浏览器先做一次 Reflow,以确保获取的是最新值。

🎨 三、什么是重绘(Repaint)?

定义: 当元素的外观改变(但没有改变布局)时,浏览器会重新绘制该元素的像素。

✅ 会触发重绘的操作有:

  • 改变 colorbackground-color

  • 改变 visibilitybox-shadowborder-color

  • 改变文字颜色、字体(不会影响布局)

⚠️ 四、重排和重绘的性能影响

  • 重排 是代价最高的,因为它会影响整个页面的渲染树,尤其是在嵌套结构较多、DOM 较大的情况下。

  • 重绘 相对轻一些,但也不免费,尤其是在动画或频繁触发时。

📌 性能杀手:频繁修改 DOM + 频繁读取布局属性 + 大量样式变动 → 卡顿、掉帧!

🛠️ 五、优化技巧

✅ 1. 减少 DOM 操作频率

// 错误写法:连续操作 DOM
for (let i = 0; i < 1000; i++) {
  let node = document.createElement('div');
  document.body.appendChild(node);
}

// 更好的方式:
let fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  let node = document.createElement('div');
  fragment.appendChild(node);
}
document.body.appendChild(fragment);

✅ 2. 批量修改样式

// 错误:会触发多次重绘/重排
el.style.width = '100px';
el.style.height = '100px';
el.style.backgroundColor = 'red';

// 正确:一次性改
Object.assign(el.style, {
  width: '100px',
  height: '100px',
  backgroundColor: 'red'
});

✅ 3. 避免同步布局读取(强制 reflow)

// 错误做法(会强制刷新布局)
box.style.width = '200px';
console.log(box.offsetHeight); // 读取会导致强制 reflow

// 更好做法:读写分离
let height = box.offsetHeight;
box.style.width = '200px';

✅ 4. 使用 classList 替代 style 连续修改

// 推荐用 classList 来统一样式变化
element.classList.add('active');

✅ 5. CSS 动画比 JS 更高效

/* 更高效的动画:GPU 加速 */
.box {
  transition: transform 0.3s ease;
  will-change: transform; /* 告诉浏览器“我要用 transform 动画了,请提前准备图层!” */
}
.box:hover {
  transform: translate3d(10px, 0, 0); /* 启用 GPU 加速 */
}

❌ 滥用 will-change(提前合成图层过多)

.card {
  will-change: transform;
}

问题: 虽然 will-change 是为了优化性能,但如果你给太多元素加上它,浏览器会:

  • 提前分配显存

  • 创建过多合成层

  • 内存暴涨甚至卡顿

✅ 正确做法:

  • 只在即将发生动画或变换的元素上使用

  • 动画完成后,移除 will-change

    // 动态加 will-change 更合理 element.style.willChange = 'transform'; setTimeout(() => { element.style.willChange = ''; }, 500);

🧠 六、动画时的最佳实践

动画时尤其要注意重绘和重排,因为它每秒要运行 60 帧(每帧 <16ms),稍微不注意就会掉帧

💡 最佳选择:使用 transformopacity 做动画,它们不会引起重排,也通常不会引起重绘,而是由合成层(compositing)来处理图层位移或透明度,性能最优。

🎁 七、总结一句话:

能不动 DOM,就别动;能不改样式,就别改;要改,就一次改完。