从上文我们知道,一旦文档被提交,渲染进程便开始解析页面和加载资源了。
从本文开始,我们分三篇文章来介绍浏览器的渲染流程
按照渲染的先后顺序会经历下面几个阶段:构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成。
本文我们主要介绍 构建 DOM 树、样式计算、布局阶段这三个部分
构建 DOM 树
渲染进程收到 HTML 文件是无法直接被渲染引擎理解的,所以要将其转化为渲染引擎能够理解的内部结构,这个结构就是 DOM。
在渲染引擎内部,使用 HTML 解析器(HTMLParser)将 HTML 字节流转换为 DOM 结构。具体的工作流程如下:
- 网络进程接收到 content-type=“text/html”响应头之后,为该请求创建一个渲染进程。
- 渲染进程准备好之后,网络进程和渲染进程之间会建立一个共享数据的管道,渲染进程通过这个管道读取数据,并同时将读取的数据“吐”给 HTML 解析器
-
解析器将数据流解析为 DOM 树。
- 类似 Javascript 的 AST 转换,解析器将数据流解析为 DOM 的第一步也是通过分词器将字节流转换为 Token
<div>test</div> StartTag test EndTag // 被解析成这样三个 Token
- 将 Token 解析为 DOM 节点
- 将 DOM 节点添加到 DOM 树中
也正是因为这种流的传输和解析,导致 HTML 解析器并不是等整个文档加载完成之后再解析的,而是网络进程加载了多少数据,HTML 解析器便解析多少数据。
与此同时,HTML 解析器维护了一个 Token 栈结构,主要用来计算节点之间的父子关系,生成的 Token 会被按照顺序压到这个栈中。具体的处理规则如下所示(过程有点像那道经典的算法题判断括号是不是匹配解法):
- 如果压入到栈中的是 StartTag Token,HTML 解析器会为该 Token 创建一个 DOM 节点,然后将该节点加入到 DOM 树中,它的父节点就是栈中相邻的那个元素生成的节点。
- 如果是文本 Token,那么会生成一个文本节点,然后将该节点加入到 DOM 树中,文本 Token 是不需要压入到栈中,它的父节点就是当前栈顶 Token 所对应的 DOM 节点。
- 如果是 EndTag 标签,比如是 EndTag div,HTML 解析器会查看 Token 栈顶的元素是否是 StarTag div,如果是,就将 StartTag div 从栈中弹出,表示该 div 元素解析完成。
现在我们有了 DOM 树之后还远远不够,浏览器还是不知道要如何去“绘画”我们的页面,下面的样式计算部分就是告诉浏览器要如何“绘画”页面
样式计算
我们知道页面中 css 有三种引入形式:
- 通过 link 引用的外部 CSS 文件
- <style> 标记内部使用
- 元素通过 style 的形式内嵌
无论哪种方式引入,像 HTML 一样,浏览器都不能理解 css,还是需要将 css 转换为浏览器能理解的结构,因此我们看下样式计算的三个步骤
- 把 CSS 转换为浏览器能够理解的结构——styleSheets
在控制台中通过 document.styleSheets 可以查看 - 转换样式表中的属性值,使其标准化
有了 styleSheets,接下来就要对其进行属性值的标准化操作。标准化操作就是将类似 ”em, rem” 计算成“px”,#000 转换成 rgba 等 - 计算出 DOM 树中每个节点的具体样式
有了标准化的属性值之后我们就可以为每个 DOM 节点计算生成样式属性值了。在计算过程中需要遵守 CSS 的继承(有些属性值会继承父节点)和层叠(优先级计算规则,这里不多介绍)两个规则。这个阶段最终输出的内容是每个 DOM 节点的样式,并被保存在 ComputedStyle 的结构内。
布局
现在,我们有 DOM 树和 DOM 树中元素的样式,但仍然不能显示页面,因为我们还不知道 DOM 元素的几何位置信息。
那么接下来就需要计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局。
这个阶段需要完成两个任务:创建布局树(Render Tree)和布局计算。
-
创建布局树
在 DOM 树中会包含部分不会显示的节点,比如 head 标签,还有使用了 display:none 等属性的元素。所以在显示之前,我们还要额外地构建一棵只包含可见元素布局树。流程如下:
- 遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中;
- 而不可见的节点会被布局树忽略掉。
- 布局计算
现在我们有了一棵完整的布局树。那么接下来,就要计算布局树节点的坐标位置了。布局的计算过程非常复杂,我们这里先跳过不讲,等到后面章节中我再做详细的介绍。
下一篇文章我们会介绍 JavaScript 脚本和 css 会不会阻塞 html 的解析和渲染?