渲染流程
导航被提交后,就进入了渲染阶段。渲染模流程在执行过程中会被划分为很多子阶段, 输入的 HTML 经过这些子阶段,最后输出像素。我们把这样的一个处理流程叫做渲染流水线
其大致流程如下图所示:

按照渲染的时间顺序,流水线可分为如下几个子阶段:构建 DOM 树、样式计算、 布局阶段、分层、绘制、分块、光栅化和合成。
构建 DOM 树
浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM树。
如图: 
DOM 和 HTML 内容几乎是一样的,但是和 HTML 不同的是,DOM 是保存在内存中树状结构, 可以通过 JavaScript 来查询或修改其内容。
样式计算
样式计算的目的是为了计算出 DOM 节点中每个元素的具体样式
1.把 CSS 转换为浏览器能够理解的结构
CSS 样式来源主要有三种:
- 通过 link 引用的外部 CSS 文件
- style标签内的css
- 元素的style熟悉内嵌的css
渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。

2. 转换样式表中的属性值,使其标准化
body { font-size: 2em }
p {color:blue;}
span {display: none}
div {font-weight: bold}
div p {color:green;}
div {color:red; }上面的 CSS 文本中有很多属性值,如 2em、blue、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值
标准化后的值 
3. 计算出 DOM 树中每个节点的具体样式
根据css的继承和层叠规则,为每个DOM节点计算出最终样式,并保存再computedStyle结构中

布局阶段
有了DOM树和DOM树的样式,还不能显示页面。因为元素的几何位置信息还不知道; 需要计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局。
1.创建布局树
DOM树含有很多不可见的元素,比如head标签,或者display:none的元素,所以再显示之前, 需要额外的构建一颗只包含可见元素的布局树
为了构建布局树,浏览器大体上完成了下面这些工作:

- 遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中;
- 而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,再比如 body.p.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。
2.布局计算
这个阶段主要计算布局树节点的坐标位置,把布局运算的结果写到布局树中保存起来;
分层
因为页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等, 为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)
渲染引擎给页面分了很多图层,这些图层按照一定顺序叠加在一起,就形成了最终的页面
图层和布局树节点之间的关系:

TIP
通常情况下,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。
渲染引擎会为以下情况的节点创建新的图层:
- 拥有层叠上下文属性的元素会被提升为单独的一层
- 需要剪裁(clip)的地方也会被创建为图层
绘制
在完成图层树的构建之后,渲染引擎会对图层树中的每个图层进行绘制
渲染引擎实现图层的绘制跟我们绘制canvas类似,会把一个图层的绘制拆分成很多小的绘制指令, 然后再把这些指令按照顺序组成一个待绘制列表,如下图所示:

实际的绘制列表可以再chrome devtools的layers标签查看:

光栅化 & 分块
绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的

当图层的绘制列表准备好之后,主线程会把该绘制列表提交commit给合成线程
在有些情况下,有的图层可以很大,比如有的页面你使用滚动条要滚动好久才能滚动到底部,但是通过视口, 用户只能看到页面的很小一部分,所以在这种情况下,要绘制出所有图层内容的话,就会产生太大的开销, 而且也没有必要。
基于这个原因,合成线程会将图层划分为图块(tile)
图块的大小通常是 256x256 或者 512x512
合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。 所谓栅格化,是指将图块转换为位图

TIP
图块是栅格化执行的最小单位
通常,栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。
快速栅格化:
渲染进程把生成图块的指令发送给 GPU,然后在 GPU 中执行生成图块的位图,并保存在 GPU 的内存中。
合成显示## 渲染流程
导航被提交后,就进入了渲染阶段。渲染模流程在执行过程中会被划分为很多子阶段, 输入的 HTML 经过这些子阶段,最后输出像素。我们把这样的一个处理流程叫做渲染流水线
其大致流程如下图所示:

按照渲染的时间顺序,流水线可分为如下几个子阶段:构建 DOM 树、样式计算、 布局阶段、分层、绘制、分块、光栅化和合成。
构建 DOM 树
浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM树。
如图: 
DOM 和 HTML 内容几乎是一样的,但是和 HTML 不同的是,DOM 是保存在内存中树状结构, 可以通过 JavaScript 来查询或修改其内容。
样式计算
样式计算的目的是为了计算出 DOM 节点中每个元素的具体样式
1.把 CSS 转换为浏览器能够理解的结构
CSS 样式来源主要有三种:
- 通过 link 引用的外部 CSS 文件
- style标签内的css
- 元素的style熟悉内嵌的css
渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。

2. 转换样式表中的属性值,使其标准化
body { font-size: 2em }
p {color:blue;}
span {display: none}
div {font-weight: bold}
div p {color:green;}
div {color:red; }上面的 CSS 文本中有很多属性值,如 2em、blue、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值
标准化后的值 
3. 计算出 DOM 树中每个节点的具体样式
根据css的继承和层叠规则,为每个DOM节点计算出最终样式,并保存再computedStyle结构中

布局阶段
有了DOM树和DOM树的样式,还不能显示页面。因为元素的几何位置信息还不知道; 需要计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局。
1.创建布局树
DOM树含有很多不可见的元素,比如head标签,或者display:none的元素,所以再显示之前, 需要额外的构建一颗只包含可见元素的布局树
为了构建布局树,浏览器大体上完成了下面这些工作:

- 遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中;
- 而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,再比如 body.p.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。
2.布局计算
这个阶段主要计算布局树节点的坐标位置,把布局运算的结果写到布局树中保存起来;
分层
因为页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等, 为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)
渲染引擎给页面分了很多图层,这些图层按照一定顺序叠加在一起,就形成了最终的页面
图层和布局树节点之间的关系:

TIP
通常情况下,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。
渲染引擎会为以下情况的节点创建新的图层:
- 拥有层叠上下文属性的元素会被提升为单独的一层
- 需要剪裁(clip)的地方也会被创建为图层
绘制
在完成图层树的构建之后,渲染引擎会对图层树中的每个图层进行绘制
渲染引擎实现图层的绘制跟我们绘制canvas类似,会把一个图层的绘制拆分成很多小的绘制指令, 然后再把这些指令按照顺序组成一个待绘制列表,如下图所示:

实际的绘制列表可以再chrome devtools的layers标签查看:

光栅化 & 分块
绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的

当图层的绘制列表准备好之后,主线程会把该绘制列表提交commit给合成线程
在有些情况下,有的图层可以很大,比如有的页面你使用滚动条要滚动好久才能滚动到底部,但是通过视口, 用户只能看到页面的很小一部分,所以在这种情况下,要绘制出所有图层内容的话,就会产生太大的开销, 而且也没有必要。
基于这个原因,合成线程会将图层划分为图块(tile)
图块的大小通常是 256x256 或者 512x512
合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。 所谓栅格化,是指将图块转换为位图

TIP
图块是栅格化执行的最小单位
通常,栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。
快速栅格化:
渲染进程把生成图块的指令发送给 GPU,然后在 GPU 中执行生成图块的位图,并保存在 GPU 的内存中。
合成显示
- 一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。
- 浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
渲染流程总结
