回流是浏览器重新计算元素几何属性并重建渲染树的过程,重绘则仅更新样式而不影响布局;回流必触发重绘,但重绘不一定引发回流。

什么是回流(reflow)和重绘(repaint)?
回流是浏览器重新计算元素几何属性(位置、尺寸)并重新构建渲染树的过程,触发条件包括读取 offsetWidth、clientHeight,或修改影响布局的样式(如 width、top、display)。重绘发生在样式改变但不触发布局时(比如 color、background-color),代价通常小于回流。但一次回流必然伴随至少一次重绘。
哪些操作会意外触发高频回流?
最典型的是在循环中反复读写 DOM 样式,形成“读-写-读-写”模式。浏览器为保证读取值准确,会在每次读取前强制同步完成上一次写入引发的回流。
- 在
for循环里连续访问element.offsetWidth和设置element.style.left - 用
getComputedStyle()获取样式后立即修改同元素的盒模型属性 - 对多个兄弟元素依次调用
appendChild(),每次插入都可能触发父容器回流
这类操作在列表滚动、动画帧内尤其危险——哪怕只改 10 个元素,也可能触发 10 次回流。
怎么批量读写、避免强制同步回流?
核心原则:把所有读操作集中前置,所有写操作集中后置。让浏览器有机会将多次写入合并为一次回流。
立即学习“Java免费学习笔记(深入)”;
const boxes = document.querySelectorAll('.box');
// ❌ 错误:读写交替
boxes.forEach(box => {
const width = box.offsetWidth; // 强制回流
box.style.left = width + 'px'; // 再次回流(可能)
});
// ✅ 正确:读一批,再写一批
const widths = Array.from(boxes).map(box => box.offsetWidth);
widths.forEach((w, i) => {
boxes[i].style.left = w + 'px';
});
更进一步,可使用 documentFragment 批量操作节点,或用 transform 替代 top/left——后者不触发回流,且能启用 GPU 加速:
- 优先用
transform: translateX(10px)而非left: 10px - 动画场景下,给元素添加
will-change: transform提前告知浏览器优化意图(但别滥用) - 避免用
table布局,其回流成本远高于flex或grid
还有哪些容易被忽略的性能陷阱?
有些优化点藏在细节里,比如 CSS 选择器深度、伪类触发时机、甚至字体加载策略。
-
:hover或:focus如果绑定在深层嵌套元素上,悬停时可能引发整块区域重绘 - 使用
font-display: swap防止字体加载期间文本重排(FOIT/FOUT)间接导致回流 - 避免在
scroll事件中直接操作样式,务必节流(throttle)或用requestAnimationFrame对齐帧率 -
visibility: hidden不触发回流,display: none会——需要隐藏但保留占位时选前者
真实项目里,回流问题往往不是单点失误,而是多个小操作叠加放大。Chrome DevTools 的 Rendering 面板打开 Paint flashing 和 Layout Shift Regions 能直观看到哪些区域在频繁重绘或跳动。











