955 字
5 分钟
回流和重绘
浏览器渲染的大致流程
TIP 在
HTML
中,每个元素都可以被视为一个’盒子’,在浏览器解析渲染过程中,会涉及到回流和重绘:
- 回流(重排): 布局引擎根据各种样式计算每个盒子在页面上的大小和位置等,也就是几何信息
- 重绘:根据计算好的几何信息,浏览器对每个盒子进行绘制
- 解析HTML,生成DOM树,解析CSS,生成CSSOM
- 将DOM树和CSSOM结合,生成渲染树(render tree)
- Layout(回流): 根据生成的渲染树,进行回流,得到节点的几何信息
- Painting(重绘): 根据得到的几何信息以及渲染树,得到节点的绝对像素
- Display: 将像素发送给GPU,展示在页面上
回流和重绘的触发时机
回流
TIP 回流这一阶段,主要是计算节点的几何信息,所以当页面布局和几何信息发生变化的时候,就会触发回流,比如下面的情况:
- 页面一开始渲染的时候(无法避免)
- 因为页面一开始相当于是空白的没有元素,后面添加了元素使页面布局发生变化
- 添加或删除DOM元素
- 显示或隐藏DOM元素
- 元素的位置发生变化
- 元素的尺寸发生变化(如大小宽高、边距等)
- 浏览器窗口尺寸变化
- 因为回流是根据视口的大小来计算节点的几何信息的
- 获取一些特定的属性值
- 比如: offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight。这些属性值都需要通过即时计算得到,所以浏览器为了获得这些值,也会进行回流
重绘
TIP
- 触发回流一定会发生重绘
- 颜色的修改
- 阴影的修改
- 文本方向的修改等
浏览器优化机制
TIP 由于每次回流(重排)都会造成额外的计算消耗,所以大多数浏览器会通过 队列化修改并批量执行来优化重排过程。浏览器会将修改操作放到队列中,直到过了一段时间或者操作达到了设置的阈值,才会清空队列。
当你获取布局信息的操作的时候,会强制队列刷新,包括前面讲到的
offsetTop
等方法都会返回最新的数据。因此浏览器不得不清空队列,触发回流重绘来返回正确的值
如何减少回流和重绘的发生
TIP
- 如果要给同个元素同时设置多个样式,使用类名去合并样式,然后通过添加/删除类名来达到目的
- 避免设置多项内联样式
- 对于复杂的动画效果,对其设置
position: fixed / absolute
,尽可能使元素脱离文档流,从而减少对其他元素的影响- 使用css3硬件加速,可以让
transform
、opacity
、filters
这些动画不会引起回流重绘- 避免使用 CSS 的
JavaScript
表达式- 在使用
JavaScript
动态插入多个节点时,可以使用DocumentFragment
(文档碎片)。创建后一次性插入,避免多次的渲染- 有时候,我们需要修改元素的布局,那么就很有可能用到上文提到过的那些需要即时计算的属性(offsetTop、offsetLeft…),如果每次循环都进行获取,性能比较糟糕,可以使用变量进行缓存,计算完毕后再提交给浏览器发出重计算请求
- 我们还可以通过通过设置元素属性
display: none
,将其从页面上去掉,然后再进行后续操作,这些后续操作也不会触发回流与重绘,这个过程称为离线操作