在 JavaScript 中,回流(Reflow) 和 重绘(Repaint) 是浏览器渲染引擎处理网页更新的两个关键过程,直接影响页面性能。以下是详细分析:
一、核心概念
重绘(Repaint)
当元素样式改变但不影响布局时(如颜色、背景色、可见性等),浏览器只需重新绘制受影响的部分。
不触发几何计算,性能开销相对较小。
回流(Reflow / Layout)
当元素的尺寸、位置、布局结构发生改变(如宽度、高度、字体大小、DOM 增删),浏览器需要重新计算整个或部分页面的几何属性,并更新渲染树。
触发后续重绘,性能开销大(可能涉及整个文档或大量节点)。
二、触发条件
常见回流操作
// 几何属性变更
elem.style.width = "100px";
elem.style.height = "100px";
// 位置相关
elem.style.margin = "10px";
elem.style.padding = "5px";
// 布局结构变化
document.body.appendChild(newElem);
parent.removeChild(elem);
// 内容变化(文本、图片尺寸)
elem.innerHTML = "New content";
imgElem.style.height = "200px"; // 图片加载后
// 窗口缩放
window.addEventListener("resize", callback);
// 读取布局属性(强制同步回流)
const width = elem.offsetWidth; // 触发回流以获取最新值
常见重绘操作
// 仅视觉样式变化
elem.style.color = "red";
elem.style.backgroundColor = "#fff";
elem.style.visibility = "hidden";
elem.style.outline = "1px solid blue";
三、性能影响
回流比重绘代价更高
回流可能导致整个渲染树重新计算,而重绘仅更新像素。
现代浏览器的优化
浏览器通过队列化批量处理回流(如多次修改后合并为一次回流)。
例外:当读取布局属性(如 offsetTop、getComputedStyle())时,浏览器会强制刷新队列执行回流,以保证返回最新值。
四、优化策略
1. 减少回流次数
// 坏实践:多次修改样式触发多次回流
elem.style.left = "10px";
elem.style.top = "20px";
// 好实践:合并修改(CSSText 或 class)
elem.style.cssText = "left: 10px; top: 20px;";
// 或
elem.className = "updated-position";
2. 使用 DocumentFragment 批量操作 DOM
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const li = document.createElement("li");
fragment.appendChild(li);
}
document.getElementById("list").appendChild(fragment); // 仅一次回流
3. 避免频繁读取布局属性
// 坏实践:读取属性触发强制回流
const width = elem.offsetWidth; // 触发回流
elem.style.width = width + 10 + "px"; // 再次回流
// 好实践:缓存布局属性
const width = elem.offsetWidth; // 只触发一次
requestAnimationFrame(() => {
elem.style.width = width + 10 + "px";
});
// requestAnimationFrame 将样式修改推迟到下一帧渲染前执行
// 此时浏览器已经完成了当前帧的所有布局计算
// 修改操作被添加到渲染队列,但不会立即触发回流
4. 提升为合成层(GPU 加速)
/* 使用 transform/opacity 避免回流 */
.animate {
transform: translateX(100px); /* 不触发回流 */
opacity: 0.5; /* 不触发回流 */
will-change: transform; /* 提示浏览器优化 */
}
5. 脱离文档流后修改
// 1. 隐藏元素(触发一次回流)
elem.style.display = "none";
// 2. 进行多次修改
updateStyles(elem);
// 3. 重新显示(再触发一次回流)
elem.style.display = "block";
五、调试工具
Chrome DevTools Performance 面板
记录操作过程,分析 "Layout"(回流)和 "Paint"(重绘)耗时。
Rendering 面板
开启 "Layout Shift Regions"(回流区域高亮为蓝色)。
开启 "Paint Flashing"(重绘区域高亮为绿色)。
六、总结
特性
回流(Reflow)
重绘(Repaint)
触发原因
布局/几何属性变化(尺寸、位置)
样式变化(颜色、背景等)
性能开销
高(重新计算布局)
中低(重新绘制像素)
关联性
回流必定触发重绘
重绘不一定触发回流
优化关键
减少布局属性读写、批量 DOM 操作
避免不必要的样式修改
核心原则:
减少强制同步布局(避免在修改样式后立即读取布局属性)。
利用 CSS 动画替代 JS 动画(优先使用 transform/opacity)。
批量操作 DOM(使用 DocumentFragment 或离线 DOM)。
通过理解回流与重绘机制,可显著提升页面渲染性能(尤其在复杂交互和动画场景中)。