花点时间了解下浏览器渲染以及回流重绘

636 阅读6分钟

最近又重新拜读了李兵老师的《浏览器工作原理与实践》,每次看都收获颇丰。这篇文章就总结一下浏览器中页面的渲染流程,顺带看看出境频率蛮高的回流重绘。

DOM树:

浏览器的页面通常来说就是从我们编写好的HTML,CSS,javaScript文件渲染来的。浏览器发送http请求拿到HTML文件后,并不能理解和使用HTML,所以需要把HTML文件转换为浏览器能够理解的结构-DOM树。DOM树是保存在内存中的对象组成的树结构,可以通过JS查询和修改

CSSOM:

浏览器获取到CSS样式表之后会构建CSSOM树,我们可以从DOM的名字推断出CSSOM是CSS对象模型,也就是保存在内存中的CSS对象,jS可以查询或者修改它。需要注意的是,构建DOM树和构建CSSOM是并行执行的,这也就解释了为什么CSS不会阻塞HTML解析

构建布局树(渲染树):

现在已经有了DOM树和CSSOM树,下一步要做的就是计算DOM树中每个节点的样式。给DOM节点应用样式又两个办法:继承和层叠。继承就是DOM节点的样式包含父节点的样式,最终会根据继承关系计算出合理的节点样式。层叠是根据CSS的层叠计算规则,合并设置到DOM节点上的多个来源的样式。最终会生成每个DOM节点的样式,也就是ComputedStyle,可以在开发工具中看到它:

image.png

现在有了DOM树中每个节点的样式,要想要渲染页面还需要知道DOM树中可见元素的几何位置。在DOM中有的元素是不可见的,比如head元素,以及设置了display:none的元素。浏览器会根据DOM树中的可见元素和CSSOM构建一个布局树(此时已经应用了样式,但是还缺少位置信息),浏览器会根据样式的宽高位置等计算出布局树中元素的位置。这样布局树有了样式和位置信息就可以准备渲染了。

分层:

浏览器中的页面是有许多图层合成的,我们知道设置了定位元素设置了z-index会产生新的层叠上下文,浏览器会为生成层叠上下文的元素单独提升为一层。类似的还有被裁剪的元素,也会生成单独的图层。但并不是所有元素都会单独分层。对于一个没分层的节点,它就属于父元素的图层(和层叠上下文的概念类似),就这样,渲染引擎根据布局树生成了对应的图层树(layerTree)。

绘制:

完成图层树的构建之后渲染引擎会对每个图层进行绘制,但是这个绘制过程只是把图层分解为绘制指令,然后按照顺序组装成待绘制列表。之前的过程全部发生在渲染主线程中。绘制列表准备好后,会进入光栅化阶段,将绘制指令生成图片,然后这些图片会被提交给合成线程(composition),合成线程和主线程互不干扰,所以使用合成线程会提高性能,降低主线程阻塞的概率,合成线程最终合成页面。

回流重绘:

通过上面的介绍我们知道了浏览器渲染一帧的大体流程:js/css解析 => 样式计算 => 布局 => 根据布局树分层并生成绘制指令 => 根据绘制指令生成图片后在合成线程合成

如果通过jS或者css修改元素的几何位置属性,改变元素的宽高或者位置,会触发重新布局,生成布局树,分层,绘制的过程,造成比较大的性能开销。我们知道回流是一个需要重新布局的过程,一般来说回流有两种类型:全量回流和增量回流全局回流是指触发了整个渲染树范围的布局,一般是同步的,比如浏览器窗口大小变化,字体变化等全局的影响,也就是说他们会在和JS代码执行的同一个事件循环内执行。增量回流一般是我们进行DOM操作引发的,浏览器会对这个过程进行优化,将批量的DOM操作引发的回流过程放入异步任务(宏任务)中执行。

但是还有一种情况会导致增量回流同步触发:比如读写offset、scroll、client以及getBoundingClientRect这些API,需要获取DOM更改后的元素位置信息,会立即引发回流,就有可能导致性能问题。

如果只改变元素的背景颜色等不会影响页面布局的属性,会直接跳过布局过程(但是可能会重新生成图层树),渲染流水线直接来到绘制过程,执行绘制以及后的渲染流水线,所以重绘的效率要比回流高。

我们之前说到的分层,css3会使用硬件加速,可以让transform、opacity这些动画不会引起回流重绘,浏览器会他们单独提升为一个层,而且transform动画变换效果只会在合成线程中合成,不会引发回流重绘。虽然动画效果不会回流重绘,但是动画开始和结束的时候,因为会生成新的图层和销毁新的图层,所以也会触发两次重绘。在这里css给了我们一个属性,willchange,我们在transfrom上加上这个属性,浏览器就可以知道,要提前为这个应用了transform的元素生成一个图层,这样就避免了重新生成图层导致的重绘

也可以看winter大佬的视频:鉴定一下网络热门面试题,了解一下这一块

另外我们之前还知道,为元素开启定位也会让元素单独提升为一个图层,而且每个图层之间的回流和重绘不会互相影响,所以开启定位可以保证不会频繁触发父元素的回流重绘,也可以起到类似的效果。

另外,开启硬件加速和willchange虽然会提高性能,但是新增图层也会增加内存占用。

参考资料:www.zoo.team/article/bro…