概览

渲染模块在执行过程中会被划分为很多子阶段,输入的 HTML 经过这些子阶段,最后输出像素。我们把这样的一个处理流程叫做渲染流水线。


1. 浏览器加载资源

用户输入 URL 时,浏览器依赖网络模块加载 html、css、js 等资源数据。

2. DOM 树的构建

html 解析器根据 html 文件,构建结点,生成 DOM 树。

DOM 和 html 的内容几乎一摸一样,唯一不同的是 DOM 以树结构保存在内存中,可由 js 修改。

document.getElementsByTagName("p")[0].innerText="black";

3. 样式计算

样式计算的目的是为了计算出 DOM 节点中每个元素的具体样式。

3.1. 创建 stylesheets(css 规则树)

渲染引擎会把获取到的 CSS 文本全部转换为 styleSheets 结构中的数据,并且该结构同时具备了查询和修改功能,这会为后面的样式操作提供基础。
css 样式来源主要有三种:

  1. 通过 link 引用的外部 CSS 文件
  2. <style> 标记内的 CSS
  3. 元素的 style 属性内嵌的 CSS

3.2. 转换样式表中的属性值,使其标准化

CSS 文本中有很多属性值,如 2em、blue、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值。

3.3. 计算出 DOM 树中每个节点的具体样式

涉及到 CSS 的继承规则和层叠规则。

继承规则:CSS 继承就是每个 DOM 节点都包含有父节点的样式。

层叠样式:层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法

总结:样式计算阶段的目的是为了计算出 DOM 节点中每个元素的具体样式,在计算过程中需要遵守 CSS 的继承和层叠两个规则。这个阶段最终输出的内容是每个 DOM 节点的样式,并被保存在 ComputedStyle 的结构内。

4. 创建布局

4.1. 创建布局树

你可能注意到了 DOM 树还含有很多不可见的元素,比如 head 标签,还有使用了 display:none 属性的元素。所以在显示之前,我们还要额外地构建一棵只包含可见元素布局树。

4.2. 布局计算

已有了一棵完整的布局树。那么接下来,就要计算布局树节点的坐标位置。

5. 分层,创建图层树

渲染引擎需要为特定的节点生成专用的图层(如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等),并生成一棵对应的图层树(LayerTree)
通常情况下,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。

6. 图层绘制

图层树的构建之后,渲染引擎会对图层树中的每个图层进行绘制,输出待绘制列表,存放绘制操作命令。

7. 栅格化(raster)操作,绘制

实际上绘制操作是由渲染引擎中的合成线程来完成的。
当图层的绘制列表准备好之后,

  1. 主线程会把该绘制列表提交(commit)给合成线程
  2. 合成线程会按照视口附近的图块来优先生成位图
  3. 实际生成位图的操作是由栅格化(将图块转换为位图)来执行的。

渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的,运行方式如下图所示:

栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。GPU 操作是运行在 GPU 进程中,这就涉及到了跨进程操作。

8. 合成和显示

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

9. 总结

  1. 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。
  2. 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
  3. 创建布局树,并计算元素的布局信息。
  4. 对布局树进行分层,并生成分层树。
  5. 为每个图层生成绘制列表,并将其提交到合成线程。
  6. 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  7. 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
  8. 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

10. 另外,重排和重绘:

10.1. 更新了元素的几何属性(重排)

如果通过 js 或者 CSS 修改元素的几何位置属性,如改变元素的宽度、高度等,会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的

10.2. 更新元素的绘制属性(重绘)

如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些

10.3. 直接合成阶段

那如果更改一个既不要布局也不要绘制的属性,渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成。
使用了 CSS 的 transform 来实现动画效果,在非主线程上合成,并没有占用主线程资源,避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。

分类: 前端开发

0 条评论

发表评论

Avatar placeholder

您的电子邮箱地址不会被公开。