浏览器渲染页面主要分为如下几步
1.根据HTML构建 DOM 树
由于浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构-DOM 树。在此步骤中,HTML解析器会将输入的HTML文档解析为对应的DOM树。
2.样式计算(Recalculate Style)
这个过程分为如下三个步骤
将CSS 文本转换为浏览器可以理解的结构-styleSheets(即CSSOM树)
由于浏览器也无法直接理解这些纯文本的 CSS 样式,所以当渲染引擎接收到 CSS 文本时,会执行转换操作。styleSheets同时具备了查询和修改功能,这会为后面的样式操作提供基础。
转换样式表中的属性值,使其标准化。
如 2em、blue、bold,这些类型数值不容易被渲染引擎理解,则会转换。将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。
计算出 DOM 树中每个节点的具体样式(即结合DOM树和CSSOM树形成渲染树)
样式计算阶段的目的是为了计算出 DOM 节点中每个元素的具体样式,在计算过程中需要遵守 CSS 的继承和层叠两个规则。这个阶段最终输出的内容是每个 DOM 节点的样式,并被保存在 ComputedStyle 的结构内。
3.布局阶段
计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局。
1.创建布局树
创建建一棵只包含可见元素布局树。浏览器会做如下操作: 遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中;而不可见的节点会被布局树忽略掉
2.布局计算
计算布局树节点的坐标位置。在执行布局操作的时候,会把布局运算的结果重新写回布局树中,所以布局树既是输入内容也是输出内容,这是布局阶段一个不合理的地方,因为在布局阶段并没有清晰地将输入内容和输出内容区分开来。针对这个问题,Chrome 团队正在重构布局代码,下一代布局系统叫 LayoutNG,试图更清晰地分离输入和输出,从而让新设计的布局算法更加简单
4.分层
为了更方便的实现页面中的复杂的效果,如3D 变换、页面滚动等,渲染引擎会为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。即将多个图层叠加在一起构成最终的页面图像。(打开 Chrome 的“开发者工具”,选择“Layers”标签即可看到页面的分层)。
图层和布局树节点之间的关系
关于图层有如下几点需要注意⚠️
- 并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层
- 拥有层叠上下文属性(定位属性、透明属性、CSS 滤镜、z-index 等)的元素会被提升为单独的一层。
- 需要剪裁(clip)的地方也会被创建为图层
5.图层绘制
构建完图层树之后之后,渲染引擎会对图层树中的每个图层进行绘制。
渲染引擎实会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表。
绘制列表中的指令其实非常简单,就是让其执行一个简单的绘制操作,比如绘制粉色矩形或者黑色的线等。而绘制一个元素通常需要好几条绘制指令,因为每个元素的背景、前景、边框都需要单独的指令去绘制。所以在图层绘制阶段,输出的内容就是这些待绘制列表。(点击开发者工具”的“Layers”标签,选择“document”层,显示的则是绘制列表)
6.栅格化(raster)操作
绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit)给合成线程。
渲染主线程和合成线程之间的关系
1.合成线程会将图层划分为图块(tile),这些图块的大小通常是 256x256 或者 512x512。
视口:通常一个页面可能很大,但是用户只能看到其中的一部分,我们把用户可以看到的这个部分叫做视口
划分图块的原因:在有些情况下,有的图层可以很大,比如有的页面你使用滚动条要滚动好久才能滚动到底部,但是通过视口,用户只能看到页面的很小一部分,所以在这种情况下,要绘制出所有图层内容的话,就会产生太大的开销,而且也没有必要。
2.合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图
渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的(图块是栅格化执行的最小单位)
合成线程提交图块给栅格化线程池
通常,栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。注意⚠️:GPU 操作是运行在 GPU 进程中,如果栅格化操作使用了 GPU,那么最终生成位图的操作是在 GPU 中完成的,这就涉及到了跨进程操作。即渲染进程把生成图块的指令发送给 GPU,然后在 GPU 中执行生成图块的位图,并保存在 GPU 的内存中
7.合成和显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
完整渲染流程图![image.png 浏览器渲染流程_第11张图片](http://img.e-com-net.com/image/info8/dbcb4f98f1d0474da6e4f2d82ee95813.jpg)
参考自:极客时间-浏览器引擎与实践