前言
这两天看了很多关于浏览器渲染过程的文章,感觉都不是特别全面。有的没有讲到合成层,有的讲了合成层的又没有前面的步骤,所以决定写一篇文章,按照自己的理解归纳总结一下,有不对的地方欢迎大家到评论区一起讨论~
浏览器大致渲染过程
- 解析HTML生成DOM树
- 解析CSS生成CSSOM树
- DOM树和CSSOM树结合,生成Render树
- Render树根据特殊属性生成Layer树
- 进行绘制
- 进行合成
1. 解析HTML生成DOM
将HTML解析为DOM树,共分为以下几个步骤:
(1)浏览器从磁盘或网络读取HTML的原始字节(字节数据),并根据文件的编码将他们转换成我们能读懂的字符串。
(2)接着讲字符串转换成Token,Token会识别出当前Token是“开始标签”、“结束标签”或“文本”等信息。
(3)接着根据”开始标签“和”结束标签“转化成Node节点,构建DOM。构建DOM的过程中,是一边解析一边生成节点对象的。换句话说,每个Token被生成后,会立马消耗这个Token来创建出节点对象。注意:带有”结束标签“标识的Token不会创建节点对象
2. 解析CSS生成CSSOM
根据DOM树浏览器就可以知道页面有什么内容,但是浏览器还需要知道这些内容该如何展示,所以需要根据CSS样式文件来构建CSSOM。构建CSSOM的过程与构建DOM的过程非常相似:
在构建CSSOM树的过程中,浏览器会确定每个节点的样式是什么,这一过程是很消耗资源的,因为一个节点的样式我们可以自行设置,也可以通过继承获得。所以CSS匹配HTML元素是一个相当复杂和有性能问题的过程。
因此DOM树要尽可能的小,CSS尽量使用id和class来匹配,并且样式千万不要过多的层叠。
3. 生成Render树
在讲解生成Render树之前,我们先来了解一些有关绘制的概念。
位图
当绘制一个图片时,需要将这个图片表示为计算机能够理解的数据结构——位图:用一个二维数组,数组中的每一个元素记录这个图片中每一个像素的具体颜色。
所以浏览器可以使用位图来记录他在某个区域所绘制的内容,绘制的过程就是根据位图所记录的颜色来填充像素。
纹理
纹理其实就是GPU(图像处理器)中的位图。
光栅化
在纹理里填充像素不是简单地遍历位图中的每个元素,然后填写元素的颜色,而是先进行光栅化。光栅化的本质是坐标变换、几何离散化、然后再填充像素。
现在的光栅化不再是对整个图像进行光栅化,而是将图像分块(tile)后,再对每个tile单独进行光栅化。光栅化完成后将像素填充进纹理,再将纹理上传至GPU。
Render Object
到现在这个阶段,我们已经有了DOM树和CSSOM树,但这两个树只是供给JS/HTML/CSS使用的,并不能直接拿来在浏览器页面或位图中进行显示,因此有了Render树。通过将DOM树和CSSOM树结合,生成Render树。Render树中的每一个Render Object与DOM树中的节点一一对应。Render Object上提供了将对应DOM节点绘制进位图的方法,它负责绘制这个DOM节点的可见内容,如:文字、背景、边框等。
4. 生成Layer树
然而,光有Render Object的内容还不能完成浏览器的绘制,因为它不能决定元素之间的覆盖关系,如position定位、z-index层序等。因此浏览器还有个层叠上下文Render Layer来决定元素之间的层叠关系。
Render Layer
Render Layer的出现不仅仅是简单的因为元素之间的层叠关系,还有比如opacity小于1等需要先回执号内容,再对内容作出一些统一处理的css效果。
总之,有层叠、半透明等情况的元素,就要从Render Object提升为Render Layer。没有这些情况,不需要提升为Render Layer的Render Object就从属于其父元素中最近的那个Render Layer。此时Render树就变成了Layer树,每个Render Layer包含了属于它的Layer和Render Object。
Layer树决定了网页绘制的层次顺序,而从属于Render Layer的Render Object决定了这个Layer的内容,所有的Render Layer和Render Object决定了网页在屏幕上最终呈现出来的内容。
到这里位置,浏览器已经可以完成绘制过程,但为了优化性能,还有Graphics Layer,我们后面会提到。
5. 绘制
在这个阶段,浏览器根据Layer树进行页面的绘制
6. 合成
因为浏览器中经常会有动画、video、canvas等的css变化,这意味着当页面中有这些元素时,因为这些元素经常变动,所以位图也会经常变动。在每秒60帧的变动里,每一次变动都要触发一次重排或重绘,这是非常大的性能消耗。因此就有了Graphics Layer和Graphics Context。
Graphics Layer和Graphics Context
Graphics Layer,又称为Compositing Layer合成层。
某些具有CSS3的3D transform的元素、在opacity、transform属性上有动画的元素、硬件加速的canvas和video元素等,会在第4步从Render Object提升为Render Layer,接着又会提升为Graphics Layer,每个不提升的Render Layer会从属于他父元素中最近的Graphics Layer。
每个Graphics Layer都有一个Graphics Context,Graphics Context会为该Layer开辟一个位图。Graphics Layer负责将自己的Render Layer及自带的Render Object绘制进位图里,然后将该位图作为纹理交给GPU进行相应的处理。
composite合成
现在GPU需要对多层纹理进行合成,同时GPU在纹理合成时对每一层的纹理都可以指定不同的合成参数,从而实现对纹理进行transform、opacity等不同的操作之后再进行合成。而对于这个过程GPU是底层硬件加速的,性能很好。最终纹理合成后再draw到屏幕上。
上述分层后合并的过程可以用一张图来描述:
影响提升至合成层的因素有:
-
3D transforms: translate3d, translateZ 等;
-
video, canvas, iframe 等元素;
-
通过 Element.animate() 实现的 opacity 动画转换;
-
通过 СSS 动画实现的 opacity 动画转换;
-
position: fixed;
-
will-change;
-
filter;
-
有合成层后代同时本身 overflow 不为 visible(如果本身是因为明确的定位因素产生的 SelfPaintingLayer,则需要 z-index 不为 auto)
-
...
到这渲染过程就结束了。
整体渲染过程如下图所示:
参考文章: