0%

浏览器原理学习笔记-页面渲染流程(上)

开始

通常,我们编写好 HTML、JavaScript、CSS等文件,经过浏览器就会显示出漂亮的页面。

image-20200526114743309

RT,左边输入html,js,css,这些数据经过中间的一个渲染过程,最终输出为屏幕上的像素。

这中间的渲染模块就是我们需要讨论的问题。

image-20200526114936334

RT,HTML 内容是由标记和文本组成。标记也称为标签,每个标签都有它自己的语义,浏览器会根据标签的语义来正确展示 HTML 内容。

由于渲染机制过于复杂,所以渲染模块在执行过程中会被划分为很多子阶段,输入的 HTML 经过这些子阶段,最后输出像素。这样的一个处理流程就叫做渲染流水线。流程示意图如下:

image-20200526120441571

按照渲染的时间顺序,流水线可以分为以下几个子阶段:构建 DOM 树 -> 样式计算 -> 布局阶段 -> 分层 -> 绘制 -> 分块 -> 光栅化 -> 合成

重点要关注的应该有以下几个阶段:

  • 开始每个阶段都有其输入的内容
  • 然后每个子阶段有其处理过程
  • 最后每个子阶段会生成输出内容

构建 DOM 树

因为浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的树结构 —— DOM 树。

image-20200526144725611

这玩意儿实际就是数据结构里面的树。在渲染流程中,就有频繁地使用树结构。

根据下图可以参考 DOM 树的构建过程:

image-20200526144850462

从图中可以看出,构建 DOM 树的输入内容实际上是一个很简单的 HTML 文件,然后经过 HTML 解析器解析,最后输出树状结构的 DOM。

为了更加直观地理解 DOM 树,可以在 console 控制台上直接打上一个”document”,这样就能看到一个完整的 DOM 树结构。

image-20200526150004652

我们可以通过 JavaScript 代码来修改 DOM 的内容。

样式计算

样式计算的目的是为了计算出 DOM 每个节点的具体样式。分三个步骤完成:

1. 把 CSS 转换为浏览器能够理解的结构

CSS 样式来源主要有以下三种:

  • 通过 link 引用外部 CSS
  • <style>内标记的 CSS
  • 元素的 <style> 属性内嵌的 CSS

和 HTML 文件一样,浏览器也没有办法直接去理解这些纯文本的 CSS 样式,所以当渲染引擎接收到 CSS 文本时,会执行一个转换的操作,将 CSS 文本转换为浏览器可以理解的结构 —— stylesheet.

我们可以在控制台中输入 document.styleSheets 来看到 styleSheets 的结构。渲染引擎会把获得到的 CSS 文本全部转换成 styleSheets 结构中的数据,并且该数据同时具备了查询和修改功能,为后面的样式操作提供基础。

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

现在已经把现有的 CSS 结构转化为浏览器可以理解的结构了,那么接下来就需要对其进行属性标准化操作了。

什么是属性值标准化,可以参考下面的 CSS 文本。

1
2
3
4
5
6
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,这些类型的数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值的标准化。

标准化之后的属性值为:

image-20200528002923033

3、计算出 DOM 树中每个节点的具体样式

这里就涉及到 CSS 的继承规则和层叠规则了。

首先是 CSS 继承。CSS 继承就是每个 DOM 节点都包含有父节点的样式

例如:

1
2
3
4
5
body { font-size: 20px }
p {color:blue;}
span {display: none}
div {font-weight: bold;color:red}
div p {color:green;}

最终应用到 DOM 节点的效果如下图所示:

image-20200528003200532

从图可以看出,所有子节点都继承了父节点的样式。比如 body 节点的 font-size 属性是 20,那么 body 节点下面的所有节点的 font-size 都等于 20。

我们可以打开 Chrome 的开发者工具,选择第一个 Element 标签,再选择 style 子标签,可以看到如下界面:

image-20200528003417076

大致可以描述为:

  • 首先,可以选择要查看的元素的样式(位于图中区域二),图中第一个区域点击对应的元素,就可以了解下面的区域查看元素的样式了。
  • 可以通过查看样式来源中查看样式的具体来源信息,看是来自于样式文件,还是 UA 样式表。
  • 最后可以通过区域 2 和区域 3 来查看样式继承的具体过程。

以上就是 CSS 继承的一些特性,样式计算过程中,会根据 DOM 节点的继承关系来合理计算样式的节点。

层叠是 CSS 的一个基本特征,它定义了如何合并来自多个源的属性值算法。它在 CSS 中处于核心地位,其全称 “层叠样式表” 正是想强调这一点。

如果你想了解每个 DOM 元素的最终计算样式,可以打开 Chrome 的“开发者工具”,选择第一个 “element” 标签,然后再选择 “Computed” 子标签。RT:

image-20200528004227762

上图红色框图显示了 html.body.div.p标签的 ComputedStyle 的值。想查看哪个元素,点击左边对应的标签就行了。

布局阶段

计算出 DOM 树可见元素的几何位置,我们把这个计算过程叫做布局。

Chrome 在布局阶段主要需要完成两个任务:创建布局树和布局计算。

1.创建布局树

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

image-20200528004625999

从上可以看出:DOM 树中所有不可见的节点都没有包含到布局树中。

为了构建布局树,浏览器大体需要完成以下的工作:

  • 遍历 DOM 树中所有可见节点,并把这些节点加到布局树中。
  • 不可见的节点会被布局树忽略掉, 如 head 标签下面的全部内容,再比如 body.p.span 这个元素,因为他有 display: none 这个属性,所以也没有被包括进去布局树之中。

2.布局计算

现在我们已经有了一颗完整的布局树,后序 Chrome 会对布局树节点的坐标位置进行一个计算。

总结

整个过程大致可以总结为:

  • 浏览器不能直接理解 HTML,所以需要进一步需要将其转换为浏览器能够理解的 DOM 树结构;
  • 生成 DOM 树后,还要根据 CSS 样式表,来计算出 DOM 树所有节点的样式
  • 最后计算出 DOM 元素的布局信息,使其都保存在布局树中