01. 回流与重绘的基础概念
在现代浏览器的渲染流水线中,回流(Layout/Reflow)和重绘(Paint/Repaint)是两类核心的渲染成本。浏览器首先将 DOM 和 CSSOM 合并成渲染树,随后进行布局计算以确定元素的几何尺寸与位置信息,这个阶段就是回流,紧接着根据渲染树的可见区域进行像素绘制,这个阶段是重绘。回流通常成本更高,因为它可能影响整个页面的布局结构和几何信息。
理解这两者的本质关系,对于优化前端性能至关重要。回流决定了盒模型和布局位置,一旦发生变更,浏览器需要重新计算页面的布局;而重绘仅涉及像素级别的绘制,不会改变布局对象的尺寸和位置。两者的顺序通常是:样式计算->回流->重绘;当仅改变颜色、背景、阴影等视觉属性而不影响布局时,通常只发生重绘。
回流与重绘的本质区别
成本差异显著:回流涉及几何、尺寸、位置等信息的重新计算,往往涉及大量计算和重绘区域的重新绘制,因此对性能的影响更大;重绘则聚焦在像素层面的更新,影响较小,尤其是在局部区域内。
触发条件不同:回流由对布局相关属性的修改触发,如 width、height、left、top 等,也包括改变 display、position、float 等导致的变动;重绘则在只改变颜色、背景、边框、阴影等视觉属性时发生。
渲染流水线中的位置
渲染流水线的精髓在于分阶段执行:风格计算 -> 布局/回流 -> 绘制/重绘 -> 合成层。理解这一点可以帮助我们将成本高的操作尽可能地分散或并行化,以减少阻塞。
在实际开发中,许多看似简单的 DOM 变更却会意外触发回流,例如读取布局属性(如 offsetWidth、getBoundingClientRect)后再写入样式,这会在同一事件循环中产生同步回流。避免在同一批次内交叉读写,是降低回流风险的关键做法之一。
02. 回流与重绘的关系与时序
关系梳理:先回流再重绘还是并发?
总体关系是:回流往往伴随重绘,因为布局改变会改变像素的绘制需求;但并非所有重绘都伴随回流。当仅修改颜色、背景、边框等不影响布局的属性时,浏览器通常直接进行重绘而不触发回流。
在页面存在复杂布局时,频繁的小范围回流会引发多轮重绘,从而导致可感知的卡顿。相反,纯视觉更新的重绘对性能的影响要小得多,尤其在复合层(compositor)已经将元素提升为独立图层时。避免跨区域的回流和大范围的重绘是提升渲染性能的核心思路。
触发时序的示例
下面的示例展示了如何由于错误的读写顺序导致强制回流:先写入样式再读取布局属性,浏览器需要重新计算布局再返回结果,造成双重工作。读写分离可以降低这类成本。
const box = document.querySelector('.box');
box.style.width = '300px'; // 写操作,可能触发重排的准备
const w = box.offsetWidth; // 读操作,强制触发回流
console.log(w);
正确的做法是把所有需要的写操作集中在一起,然后一次性触发浏览器的布局与绘制,避免在同一事件循环中混合读写,从而减少回流次数。
03. 实际性能成本与优化策略
减少回流的具体策略
在大规模 DOM 操作中,批量更新比逐步更新更高效;将多次修改合并成一次重新计算布局的机会,通常能显著降低回流的次数。优先使用对布局影响较小的属性进行修改,并尽量避免涉及尺寸和位置的变更。
加入对 contain、will-change、以及独立合成层的使用,可以让浏览器在局部区域内完成更新而不必重排整页。通过合理的样式选择,可以降低回流的粒度和范围,提高渲染的并行度。

减少重绘的策略
使用合成层(如 transform、opacity)实现动画,避免直接操作影响布局的属性导致的重绘;慎用遮罩、阴影、渐变等高成本的视觉效果,尤其是在大面积区域。
另外,避免在高频渲染区域内做大量的视觉变更,例如在滚动区域、视口内的元素上进行频繁的颜色或背景切换,改用简单的、可复用的视觉策略可以降低重绘频次。
/***** 使用变换代替位移以避免回流/重绘 *****/
.panel { transform: translateZ(0); /* 触发合成层,对动画友好 */will-change: transform; /* 提示浏览器即将发生变换 */
}
/***** 使用 contain 限定渲染影响范围 *****/
.container {contain: layout paint style; /* 限制重排与重绘影响的区域 */
}
04. 开发工具与诊断方法
ChromeDevTools 的关键面板
Performance 面板是排查回流与重绘的最有力工具之一。你可以通过记录页面加载和交互过程,定位长时间运行的脚本、布局、绘制和合成阶段,从而确定影响渲染的热点区域。
此外,Elements 面板的“Computed”与“Layout”视图可以帮助你查看某些属性变化对布局造成的影响,确定哪些属性修改会触发回流。颜色与尺寸相关的更改最易引发回流,而仅改变颜色通常只触发重绘。
如何定位高耗时的回流
在 Performance 时间线中,注意标注为 layout 的阶段,这些阶段表示浏览器进行布局运算;以及标注为 paint 的阶段,表示像素绘制。聚焦在包含大量 layout 的帧上,通常意味着回流成本较高。
结合 DOM 变更前后的对比,可以快速判断哪些操作触发了回流,例如对元素尺寸的改动、对父容器的改变、以及强制读取布局的行为。尽量减少一次渲染周期内的多次回流,并将更新合并到一个批次中。
05. 常见误区与最佳实践
误区1:只优化动画就能解决回流与重绘问题
动画带来的性能影响不仅仅取决于 FPS,背后还是回流与重绘的成本分布。即使动画帧率高,如果动画涉及大量的布局变化,回流成本也会上升,因此需要在动画设计时尽量使用对布局无关的属性。
常见的误区是忽视批量更新,单次连续的小变更容易在后续帧中累积成回流。组织代码以批量化修改 DOM,通常比逐步逐次修改更高效。
误区2:某些看似轻量的操作不会影响性能
并非所有视觉变更都只是“轻量”操作;当涉及到大范围区域的颜色更新、背景图片替换或大规模重排时,潜在成本同样不可忽视。局部更新并不等于无成本更新,需要结合渲染层级进行评估。
在实际开发中,最佳实践是通过 性能分析工具 持续监测回流与重绘的成本分布,避免盲目优化而错过真正的瓶颈。


