Skip to content
目录

渲染流程

导航被提交后,就进入了渲染阶段。渲染模流程在执行过程中会被划分为很多子阶段, 输入的 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. 转换样式表中的属性值,使其标准化

css
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. 转换样式表中的属性值,使其标准化

css
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 的内存中。

合成显示

  1. 一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。
  2. 浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。

渲染流程总结

备案号: 浙ICP备2023000081号