【每日一瞥】重绘与重排(Repaint、Reflow)

【问题】介绍下重绘和回流(Repaint & Reflow),以及如何进行优化

重绘和重排(也叫回流)是浏览器渲染机制中的两个重要步骤和阶段,对应的其消耗的成本也不一致,为了减少浏览器的渲染成本,了解这两个概念还是很重要的。

重绘与重排

何谓重绘?何谓重排?

由于节点的几何属性发生改变或者由于样式发生改变而不会影响布局的,称为重绘,例如outline, visibility, color、background-color等,只涉及样式的修改,不涉及布局改变的属性。

对应的布局或者几何属性需要改变,造成整个dom结构都需要重新计算的,称为重排(也叫回流)

重排一定会触发重绘,而重绘不一定会重排

浏览器渲染机制

根据Google的文档

浏览器渲染过程如下:

1.解析HTML,生成DOM树,解析CSS,生成CSSOM树。
2.DOM 树与 CSSOM 树合并后形成渲染树,渲染树只包含渲染网页所需的节点。
3.布局计算每个对象的精确位置和大小。
4.最后一步是绘制,使用最终渲染树将像素渲染到屏幕上。

重排就是第三步的计算布局,而重绘指第四步的绘制,所有才会有重排一定会触发重绘,而重绘不一定会重排。

浏览器渲染最重要的一步就是生成渲染树。

1.从DOM树的根节点开始遍历每个可见节点。
2.对于每个可见的节点,找到CSSOM树中对应的规则,并应用它们。
3.根据每个可见节点以及其对应的样式,组合生成渲染树。

由于最终展示给用户的节点只是我们dom树中的一部分,所以这一步需要过滤一些不展示的节点,比如script和link这类没有视觉上的实际意义的节点以及display:none的不可见节点。

何时发生重排重绘

重排计算的是节点的位置和大小形状等几何信息,所以当节点的位置和几何信息变化时就会触发局部甚至整个document的重排,比如

添加或删除可见的DOM元素
元素的位置发生变化
元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
页面一开始渲染的时候(这肯定避免不了)
浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

而一些无关位置和大小的属性变化,比如outline, visibility, color、background-color等,则不会触发重排,只会触发重绘。

浏览器优化

首先明确的一点,无论重绘,还是重排都是会造成浏览器的计算消耗,重排会造成比重绘更多的消耗,所以我们要尽量避免重绘与重排,尤其是重排。

避免频繁操作样式

避免频繁操作样式,将多次样式修改尽可能合并到一处,可以使用cssText或者直接修改class,避免采用这类写法。

el.style.padding = '5px';  
el.style.borderLeft = '1px';  
el.style.borderRight = '2px';

避免频繁操作DOM
同样的道理,操作DOM大概率会造成重排,所以对于插入或者删除DOM尽量少,
比较推荐的修改DOM的方法是创建一个documentFragment,修改的话,可以复制一个dom出来,在它上面应用所有DOM操作,最后再把它添加到文档中。

对于以上的两点,大多数现代浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值。所以即使多次操作,也只会触发一次重排或重绘。

然而,有一点注意,当你获取布局信息的操作的时候,会导致浏览器强制清空队列,进行强制同步布局。所以这里我们要避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
具体的属性:

offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
getComputedStyle()
getBoundingClientRect

对于复杂动画效果,使用绝对定位让其脱离文档流
只要脱离了文档流,这个元素的任何变化都不会影响其他元素的布局,要重排也是自己重排,不影响其他,所以对于会频繁变动的元素比如动画,那尽量脱离文档流。

尽量使用不影响布局的属性
比如使用 transform 替代 top,left等,使用 visibility 替换 display: none (尤其是在周期内会频繁变动的,比如查看和收起)。
在vue中要慎重使用v-if和v-show。

CSS3 硬件加速(GPU加速)

使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起重排重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起重排重绘的,不过它还是可以提升这些动画的性能。