Skip to content
 

重排和重绘 ​

更新: 9/11/2026字数: 0 字 时长: 0 分钟

在前端性能优化中,重排(Reflow,也称回流)和重绘(Repaint)是浏览器渲染页面时最核心的两个概念。搞懂它们的触发机制和优化策略,是写出流畅页面的关键。

一、 核心概念与区别 ​

浏览器渲染页面的大致流程为:HTML/CSS 解析 → 生成 DOM/CSSOM 树 → 构建渲染树(Render Tree) → 布局/重排(Layout/Reflow) → 绘制/重绘(Paint/Repaint) → 合成(Composite)。

概念含义触发原因性能开销
重排 (Reflow)浏览器重新计算元素的几何属性(位置、大小),并重新构建渲染树的一部分或全部。元素的几何尺寸、位置、显示状态(如 display)、页面结构发生变化。极高(引发链式反应,影响祖先与相邻元素)
重绘 (Repaint)浏览器重新绘制元素的外观,但不影响其几何布局。元素的外观样式发生变化(如 color、background-color)。较低(只需重新填充像素点)

关键规律:重排一定会触发重绘,但重绘不一定会触发重排。

二、 哪些操作会触发重排与重绘? ​

1. 触发重排(Reflow)的操作 ​

  • 页面首次渲染(无法避免)。
  • DOM 结构改变:添加、删除、移动 DOM 节点。
  • 元素几何属性变化:修改 width、height、padding、margin、border 等。
  • 位置与显示变化:修改 top、left、display: none(注:visibility: hidden 只触发重绘)。
  • 字体与文本变化:修改 font-size、text-align 或用户输入内容。
  • 调整浏览器窗口大小:触发 resize 事件。
  • 读取特定的 DOM 属性或调用 API(强制刷新渲染队列):
  • 偏移量及尺寸:offsetTop / offsetLeft / offsetWidth / offsetHeight
  • 滚动属性:scrollTop / scrollLeft / scrollWidth / scrollHeight
  • 客户区尺寸:clientTop / clientLeft / clientWidth / clientHeight
  • 调用 API:getComputedStyle()、getBoundingClientRect()

2. 仅触发重绘(Repaint)的操作 ​

修改不涉及几何属性的外观特征:

  • color、background-color、border-style
  • visibility: hidden
  • box-shadow、outline

三、 性能优化实战技巧 ​

为了避免页面卡顿(丢帧),日常开发中应尽量减少重排和重绘的次数:

1. CSS 优化策略 ​

  • 使用 transform 代替 top/left 做动画:transform 和 opacity 会开启硬件加速(GPU 合成),只触发 Composite(合成) 阶段,既不重排也不重绘。
  • **使用 visibility: hidden 代替 display: none**:前者只重绘,后者会触发重排。
  • 批量修改样式:使用 class 类名一次性改变样式,而不是频繁写 element.style.xxx。
  • 离屏渲染:设置 position: absolute 或 fixed 使动画元素脱离文档流,使其重排不影响其他普通文档流节点。

2. JS / DOM 操作优化策略 ​

  • DOM 批量更新(DocumentFragment / 离屏):
javascript
// ❌ 差的做法:循环 1000 次,触发 1000 次重排
for (let i = 0; i < 1000; i++) {
  const el = document.createElement('div');
  document.body.appendChild(el);
}

// ✅ 好的做法:使用 DocumentFragment 一次性插入
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  const el = document.createElement('div');
  fragment.appendChild(el);
}
document.body.appendChild(fragment); // 仅触发 1 次重排
  • 避免强制同步布局(Forced Synchronous Layout): 避免在循环中先写入 DOM,再读取位置属性。
javascript
// ❌ 差的做法:读写交替,强制浏览器在循环中频繁重排
for (let i = 0; i < box.length; i++) {
  box[i].style.width = container.offsetWidth + 'px'; // 读取引发强制重排,紧接着写入
}

// ✅ 好的做法:读写分离
const width = container.offsetWidth; // 先统一读取
for (let i = 0; i < box.length; i++) {
  box[i].style.width = width + 'px'; // 再统一写入
}
  • **使用 requestAnimationFrame**:将频繁的 DOM 读写操作放到下一帧渲染前统一执行。