从上文我们知道,一旦文档被提交,渲染进程便开始解析页面和加载资源了。
从本文开始,我们分三篇文章来介绍浏览器的渲染流程
按照渲染的先后顺序会经历下面几个阶段:构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成。
本文我们主要介绍 构建 DOM 树、样式计算、布局阶段这三个部分
构建DOM树
渲染进程收到 HTML 文件是无法直接被渲染引擎理解的,所以要将其转化为渲染引擎能够理解的内部结构,这个结构就是 DOM。
在渲染引擎内部,使用 HTML 解析器(HTMLParser)将 HTML 字节流转换为 DOM 结构。具体的工作流程如下:
- 网络进程接收到content-type=“text/html”响应头之后,为该请求创建一个渲染进程。
- 渲染进程准备好之后,网络进程和渲染进程之间会建立一个共享数据的管道,渲染进程通过这个管道读取数据,并同时将读取的数据“吐”给 HTML 解析器
-
解析器将数据流解析为 DOM树。
- 类似Javascript的AST转换,解析器将数据流解析为 DOM的第一步也是通过分词器将字节流转换为 Token
testStartTag 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 文件
-
test1test2