理解了重排(回流)与重绘的定义后,最关键的问题是:什么操作会触发它们?以及,它们对性能到底有多大影响?
触发条件:何时会发生重排与重绘?
必定触发重排(回流)的操作——这些操作改变了元素的几何属性或页面布局:
- 添加、删除、移动可见的 DOM 元素。
- 修改元素的尺寸、边距、边框、内外边距(
width、height、margin、padding、border)。 - 更改元素的定位方式或位置(
position、top、left、display)。 - 读取某些需要实时计算的属性:如
offsetTop、offsetLeft、offsetWidth、offsetHeight、scrollTop、getComputedStyle()等。因为浏览器为了返回准确值,必须立即触发一次重排来确保布局是最新的。 - 改变窗口大小(resize 事件),或者改变字体大小导致内容区域变化。
- 激活 CSS 伪类(如
:hover中改变了布局属性)。 - 初次渲染:页面首次完成 DOM 和 CSSOM 解析后,初次构建渲染树并计算布局。
仅触发重绘而不会重排的操作——这些操作只改变外观,不影响布局:
- 改变颜色、背景色、文字颜色、边框颜色等(
color、background-color、border-color)。 - 改变不影响几何属性的可见性/透明度(
visibility、opacity,注意:display: none会导致重排)。 - 修改
box-shadow、outline等纯视觉效果属性。
不触发任何重排重绘的操作(仅在合成层上触发):
- 通过
transform移动或缩放、opacity改变透明度(配合 will-change 或 3D 特性使元素提升到合成层)。 - 通过 CSS 动画或过渡操作
transform和opacity等合成属性。
关键区分:重排必然引起重绘(因为布局变了,外观也要更新),而重绘不一定引起重排。
性能影响:重排与重绘到底有多“贵”?
重排(回流)是代价极高的操作
- 成本来源:重排需要重新计算渲染树中所有受影响节点的几何属性,然后重新布局。这一过程涉及大量 CPU 计算,尤其当页面结构复杂、嵌套层级深时,一个元素的尺寸变化可能导致其父元素、子元素甚至后续兄弟元素都需要重新计算,形成“连锁重排”。
- 阻塞用户交互:由于 JavaScript 引擎与渲染引擎互斥(在浏览器主线程上运行),一次耗时较长的重排会阻塞主线程,导致页面无法响应用户点击、滚动,体现为“卡顿”或“掉帧”。如果在一个滚动事件或动画执行期间频繁强制重排,体验会急剧恶化。
- 组合开销:浏览器可能将多次重排合并成一次,但如果我们混合读写样式属性,这个优化会被破坏。例如:先设置
el.style.width = '100px',然后立即读取el.offsetWidth,浏览器为了给出准确的offsetWidth,不得不立刻执行重排;如果夹在读写之间的模式循环多次,就会引发多次强制同步重排,造成巨大性能瓶颈。
重绘的成本相对低,但仍不可忽视
- 成本来源:重绘只涉及像素绘制阶段,不需要重新计算布局。现代浏览器能将光栅化、绘制等任务交由 GPU 在合成器线程上完成,因此代价小于重排。
- 累积效应:如果短时间内触发大量元素重绘(例如,修改整个列表的颜色),同样会引发性能问题。浏览器需要重新绘制每个受影响元素的像素,大量绘制面积会产生可感知的卡顿。
合成层上的操作几乎无成本
- 通过
transform和opacity实现动画,并由 GPU 合成的操作,既不会触发重排也不会触发重绘,只涉及合成层的合并,这是在移动设备上也能达到 60fps 流畅动画的关键。这也是现代前端框架和动画库极力推荐使用transform代替top/left等属性的原因。
实用优化策略概要
基于触发条件和性能影响,实际开发中的核心优化思路就是:尽量避免或减少重排,尤其避免在密集操作中强制同步重排。
- 批量读写 DOM:不要在一个循环中不断读取布局属性又修改样式。可以先用变量缓存所有布局信息,再统一修改,或使用
requestAnimationFrame将读取和写入分隔到不同阶段。 - 使用 CSS 类代替多个内联样式修改:一次性切换类名,将多个样式修改合并为一次重排。
- 离线 DOM 操作:先将元素从文档流中脱离(如
display: none或使用DocumentFragment),在其中进行多次修改,再将其放回文档,这样只会触发一次重排。 - 利用合成层提高动画性能:对需要动画的元素使用
will-change: transform或transform: translateZ(0)来创建独立的合成层,在合成层上进行变化,避免影响主文档的重排重绘。 - 避免使用
table布局:表格的灵活性设计导致其内部任一单元格的重排容易引起整个表格的重新计算,非常昂贵。
简单记忆原则:只要可能影响元素位置和尺寸的操作,就要警惕重排成本;纯颜色、背景等视觉变化只会重绘;而现代动画请交给 transform 和 opacity。在下一节中,我们将深入探讨合成层与 GPU 加速的具体原理,帮你更系统地运用这些优化手段。