我们首先来回顾一下渲染流水线
的流程:
接下来,我们将来以此为依据来介绍重绘和回流,以及让更新视图的另外一种方式——合成。
回流
首先介绍回流
。回流
也叫重排
。
触发条件
简单来说,就是当我们对 DOM 结构的修改引发 DOM 几何尺寸变化的时候,会发生回流
的过程。
具体一点,有以下的操作会触发回流:
一个 DOM 元素的几何属性变化,常见的几何属性有
width
、height
、padding
、margin
、left
、top
、border
等等, 这个很好理解。使 DOM 节点发生
增减
或者移动
。读写
offset
族、scroll
族和client
族属性的时候,浏览器为了获取这些值,需要进行回流操作。调用
window.getComputedStyle
方法。
回流过程
依照上面的渲染流水线,触发回流的时候,如果 DOM 结构发生改变,则重新渲染 DOM 树,然后将后面的流程(包括主线程之外的任务)全部走一遍。
相当于将解析和合成的过程重新又走了一篇,开销是非常大的。
重绘
触发条件
当 DOM 的修改导致了样式的变化,并且没有影响几何属性的时候,会导致重绘
(repaint
)。
重绘过程
由于没有导致 DOM 几何属性的变化,因此元素的位置信息不需要更新,从而省去布局的过程。流程如下:
跳过了生成布局树
和建图层树
的阶段,直接生成绘制列表,然后继续进行分块、生成位图等后面一系列操作。
可以看到,重绘不一定导致回流,但回流一定发生了重绘。
合成
还有一种情况,是直接合成。比如利用 CSS3 的transform
、opacity
、filter
这些属性就可以实现合成的效果,也就是大家常说的GPU加速。
GPU加速的原因
在合成的情况下,会直接跳过布局和绘制流程,直接进入非主线程
处理的部分,即直接交给合成线程
处理。交给它处理有两大好处:
能够充分发挥
GPU
的优势。合成线程生成位图的过程中会调用线程池,并在其中使用GPU
进行加速生成,而GPU 是擅长处理位图数据的。没有占用主线程的资源,即使主线程卡住了,效果依然能够流畅地展示。
实践意义
知道上面的原理之后,对于开发过程有什么指导意义呢?
- 避免频繁使用 style,而是采用修改
class
的方式。 - 使用
createDocumentFragment
进行批量的 DOM 操作。 - 对于 resize、scroll 等进行防抖/节流处理。
- 添加 will-change: tranform ,让渲染引擎为其单独实现一个图层,当这些变换发生时,仅仅只是利用合成线程去处理这些变换,而不牵扯到主线程,大大提高渲染效率。当然这个变化不限于
tranform
, 任何可以实现合成效果的 CSS 属性都能用will-change
来声明。这里有一个实际的例子,一行will-change: tranform
拯救一个项目,点击直达。