reflow、repaint 与 transform 为何高效
回流与重绘分别对应布局树与绘制指令的重新计算,以及为何 transform 动画大多不拖累渲染主线程。
reflow、repaint 与 transform 为何高效
上一篇 浏览器渲染流水线 讲的是「从零画一页」。页面跑起来之后,改 DOM 或样式会触发 局部重算。面试里常说的 回流(reflow)、重绘(repaint),指的就是流水线里哪些阶段要重跑。
什么是 reflow(回流)?
Reflow 的本质是重新计算布局树(Layout)。
凡是会影响布局树的操作——改宽高、边距、显示方式、插入删除节点等——都需要重新做 Layout 及之后的阶段(分层、绘制、分块、光栅化、合成)。
浏览器会合并多次改动
连续多次改布局相关属性时,浏览器往往 先合并,等当前 JS 执行块结束后再统一算布局,避免同一帧里反复 reflow。
因此:改属性触发的 reflow 常常是异步批量的。
读布局属性会强制同步 reflow
如果 JS 在改完布局后又 读取 offsetWidth、getBoundingClientRect() 等依赖最新几何信息的属性,浏览器必须 立刻算出最新布局,否则会返回过期数据。
所以常见性能建议:先读后写,或批量改样式,避免读写交错。
读写交错可能触发强制同步布局
javascriptconst el = document.querySelector('.box')
el.style.width = '200px'
// 读宽度会迫使浏览器立即 reflow
console.log(el.offsetWidth)
el.style.width = '300px'什么是 repaint(重绘)?
Repaint 的本质是重新生成绘制指令(Paint)及之后步骤。
改的是 可见样式但不改布局 时——例如 color、background、visibility(在仍占位的情况下)——可能只需 Repaint,不必完整 reflow。
但 布局信息本身也是可见结果的一部分,所以:
Reflow 一定会引起 Repaint。
| 操作类型 | 典型属性 | 至少触发的阶段 |
|---|---|---|
| Reflow | width、margin、display | Layout → … → Draw |
| Repaint | color、background | Paint → … → Draw |
为什么 transform 效率高?
transform 不改变布局树,也 不改变绘制指令的生成方式(不触发典型意义上的 layout / paint 重算)。它主要影响流水线最后的 Draw(合成) 阶段:在合成线程里对已有位图做位移、旋转、缩放。
因此:
transform变化时,渲染主线程 往往仍很空闲- 主线程再忙,也不应挡住 合成线程上的 transform 动画
对比:用 top / left 做位移动画会改布局,通常走 Reflow 路径,代价大得多。
注意: 对比的前提是 画面一样、实现不同。下面 demo 两个小球同步左右移动,左边用 transform,右边用 left;视觉上应一致,差别在每一帧走合成还是 Layout。
transform vs left:同动画、不同实现
Live Preview · 可编辑代码
小结
- Reflow = 重算布局树,代价高;浏览器会批量,但 读布局属性会强制同步。
- Repaint = 重算绘制及之后;reflow 必然连带 repaint。
- transform 主要在合成阶段完成,适合做位移、旋转、缩放动画;位移动画优先
transform,慎用top/left。