一、回流(Reflow):布局几何信息的重新计算
1. 定义与触发条件
回流指浏览器根据元素的几何属性(位置、尺寸、间距等)重新计算布局,并更新页面物理结构的过程。触发场景包括:
- 首次渲染:页面首次加载时必然触发全量回流。
- DOM 结构变化:添加 / 删除元素、移动元素位置(如
appendChild
、修改position
)。 - 几何属性修改:修改
width
、height
、padding
、margin
、border
、font-size
等影响布局的属性。 - 浏览器环境变化:窗口 resize、滚动、切换暗黑模式(影响字体等样式)。
2. 回流的影响范围
- 全量回流:修改
<html>
或<body>
的属性,导致整个页面重新布局(成本极高)。 - 部分回流:仅修改局部元素,如某个
div
的尺寸,但可能因布局嵌套影响其父元素或兄弟元素(如 Flex 布局中子元素尺寸变化可能影响父容器)。
3. 性能消耗原理
回流需浏览器重新计算 渲染树中所有相关元素的几何位置,并更新 布局树(Layout Tree)。此过程依赖主线程计算,若频繁触发会导致页面卡顿(如 60fps 场景下,单次回流需控制在 16ms 以内)。
二、重绘(Repaint):元素视觉样式的更新
1. 定义与触发条件
重绘指元素样式改变但不影响布局时,浏览器重新绘制元素外观的过程。触发场景包括:
- 修改
color
、background
、border-style
、box-shadow
等不影响几何位置的属性。 - 更改文字内容(文字尺寸不变时仅触发重绘,若尺寸变化则触发回流)。
- 隐藏元素(
visibility: hidden
触发重绘,display: none
触发回流)。
2. 重绘与回流的关系
- 回流必触发重绘:布局变化后元素位置 / 尺寸改变,必然需要重新绘制。
- 重绘不一定触发回流:如仅修改颜色时,元素几何位置不变,无需重新布局。
3. 性能消耗原理
重绘需浏览器更新元素的 像素级样式,并将结果绘制到图层上。虽然比回流成本低,但大量重绘仍会占用主线程资源(如动画场景中频繁重绘可能导致掉帧)。
三、回流与重绘的优化策略
1. 减少触发频率:批量操作 DOM
- 使用文档碎片(DocumentFragment):
js
const fragment = document.createDocumentFragment(); for (let i = 0; i < 100; i++) { const div = document.createElement('div'); fragment.appendChild(div); } document.body.appendChild(fragment); // 仅触发一次回流
- 修改样式时使用类名批量更新:
css
.update-style { width: 200px; height: 200px; background: red; }
js
element.classList.add('update-style'); // 一次操作触发一次回流,而非多次
2. 避免触发回流的属性读写操作
- 先读再写会强制触发回流:
js
// 反例:先读取offsetWidth(触发回流),再修改样式(再次触发回流) console.log(element.offsetWidth); // 强制回流 element.style.width = '200px'; // 再次回流
优化:将读操作与写操作分离:js
// 先修改样式,再统一读取(或先读取再批量修改) element.style.width = '100px'; element.style.height = '100px'; console.log(element.offsetWidth); // 此时仅触发一次回流
3. 利用合成层(Compositing Layer)规避回流 / 重绘
- 对频繁动画的元素添加属性,使其成为独立合成层(由 GPU 处理,不阻塞主线程):
css
.animate-element { transform: translateZ(0); /* 触发合成层 */ will-change: transform; /* 提前告知浏览器准备合成层 */ /* 或使用 opacity 等属性 */ }
合成层的动画仅需 GPU 重新合成图层,无需触发回流 / 重绘,性能优势显著。
4. 使用 CSS 变量减少样式修改次数
- 通过 CSS 变量动态更新样式,减少 DOM 操作:
css
:root { --bg-color: blue; } .box { background-color: var(--bg-color); }
js
document.documentElement.style.setProperty('--bg-color', 'red'); // 一次操作更新所有使用该变量的元素
四、性能监控:识别回流 / 重绘瓶颈
- 浏览器开发者工具:
- 在 Chrome 的 Performance 面板中,查看 Layout 和 Paint 阶段的耗时。
- 在 Rendering 面板中勾选 Paint flashing(重绘区域高亮)和 Layout shift regions(回流区域高亮),直观定位问题元素。
- 代码埋点监控:
使用performance.now()
测量关键操作的耗时,例如: -
js
const start = performance.now(); // 可能触发回流/重绘的操作 const end = performance.now(); console.log(`操作耗时:${end - start}ms`);
五、总结:回流与重绘的核心差异
维度 | 回流(Reflow) | 重绘(Repaint) |
---|---|---|
定义 | 重新计算元素布局(几何位置与尺寸) | 重新绘制元素视觉样式(颜色、阴影等) |
触发条件 | 布局相关属性修改、DOM 结构变化、窗口 resize | 非布局属性修改(如颜色、背景) |
性能消耗 | 高(涉及布局树更新,阻塞主线程) | 中(仅图层绘制,不阻塞布局) |
优化核心 | 减少布局修改频率,批量操作 DOM | 减少样式修改频率,利用合成层规避绘制 |
理解回流与重绘机制是前端性能优化的基础,通过合理设计 DOM 结构、减少不必要的样式修改,并利用浏览器底层特性(如合成层),可显著提升页面流畅度。