前端学习笔记之 浏览器如何渲染页面?
从 HTML 到 DOM
1. 字节流解码
把字节数据解码成字符数据的过程。
2. 输入流预处理
把字符数据进行统一格式化的过程。
3. 令牌化
令牌化包含两步:第一步是将字符数据转化成令牌(Token),第二步是解析 HTML 生成 DOM 树。
html 代码的标记过程:
- 初始化为“数据状态”(Data State);
- 匹配到字符 <,状态切换到 “标签打开状态”(Tag Open State);
- 匹配到字符 !,状态切换至 “标签声明打开状态”(Markup Declaration Open State),后续 7个字符可以组成字符串 DOCTYPE,跳转到 “DOCTYPE 状态”(DOCTYPE State);
- 匹配到字符为空格,当前状态切换至“DOCTYPE 名称之前状态”(Before DOCTYPE Name State);
- 匹配到字符串 html,创建一个新的 DOCTYPE 标记,标记的名字为 “html” ,然后当前状态切换至 “DOCTYPE 名字状态”(DOCTYPE Name 之State);
- 匹配到字符 >,跳转到 “数据状态” 并且释放当前的 DOCTYPE 标记;
- 匹配到字符 <,切换到 “标签打开状态”;
- 匹配到字符 h,创建一个新的起始标签标记,设置标记的标签名为空,当前状态切换至 “标签名称状态”(Tag Name State);
- 从字符 h 开始解析,将解析的字符一个一个添加到创建的起始标签标记的标签名中,直到匹配到字符 >,此时当前状态切换至 “数据状态”并释放当前标记,当前标记的标签名为 “html” 。
- 解析后续的方式与前面一致,创建并释放对应的起始标签标记,解析完毕后,当前状态处于 “数据状态” ;
- 匹配到字符串 “标记” ,针对每一个字符,创建并释放一个对应的字符标记,解析完毕后,当前状态仍然处于 “数据状态” ;
- 匹配到字符 <,进入 “标签打开状态” ;
- 匹配到字符 /,进入 “结束标签打开状态”(End Tag Open State);
- 匹配到字符 b,创建一个新的结束标签标记,设置标记的标签名为空,当前状态切换至“标签名称状态”(Tag Name State);
- 重新从字符 b 开始解析,将解析的字符一个一个添加到创建的结束标签标记的标签名中,直到匹配到字符 >,此时当前状态切换至 “数据状态”并释放当前标记,当前标记的标签名为 “body”;
- 解析 的方式与 一样;
- 所有的 html 标签和文本解析完成后,状态切换至“数据状态” ,一旦匹配到文件结束标志符(EOF),则释放 EOF 标记。
遇到 script 标签时的处理:
- 内联代码:解析过程暂停,执行权转给JavaScript 脚本引擎,等待JavaScript执行完成。如果遇到调用document.write() 函数,此时渲染引擎会回到第二步,将这些代码加入字符流,重新进行解析。
- 外链脚本:根据标签属性来执行对应的操作。
4. 构建 DOM 树
浏览器在创建解析器的同时会创建一个 Document 对象。
构建 DOM 树的步骤:
- 进入初始状态 “initial” 模式;
- 树构建器接收到 DOCTYPE 令牌后,树构建器会创建一个 DocumentType节点附加到 Document 节点上,DocumentType 节点的 name 属性为 DOCTYPE 令牌的名称,切换到“before html” 模式;
- 接收到令牌 html 后,树构建器创建一个 html 元素并将该元素作为 Document的子节点插入到 DOM 树中和开放元素栈中,切换为 “before head” 模式;
- 虽然没有接收到 head令牌,但仍然会隐式地创建 head 元素并加到 DOM 树和开放元素栈中,切换到“in head”模式;
- 将开放元素栈中的 head元素弹出,进入 “after head”模式;
- 接收到 body 令牌后,会创建一个 body 元素插入到 DOM树中同时压入开放元素栈中,当前状态切换为 “in body” 模式;
- 接收到字符令牌,创建 Text 节点,节点值为字符内容“标记”,将Text 节点作为 body 元素节点插入到 DOM 树中;
- 接收到结束令牌 body,将开放元素栈中的 body 元素弹出,切换“after body” 模式;
- 接收到结束令牌 html,将开放元素栈中的 html 元素弹出,切换至 “after after body” 模式;
- 接收到 EOF 令牌,树构建器停止构建,html 文档解析过程完成。
从 DOM 到渲染
有了 DOM 树和 CSSOM 树之后,渲染引擎就可以开始生成页面了。
5. 构建渲染树
将DOM 树包含的结构内容与 CSSOM 树包含的样式规则合并成一棵渲染树。这个过程会从 DOM 树的根节点开始遍历,然后在 CSSOM 树上找到每个节点对应的样式。
6. 布局
布局就是计算元素的大小及位置。
布局完成后会输出对应的“盒模型”,它会精确地捕获每个元素的确切位置和大小,将所有相对值都转换为屏幕上的绝对像素。
7. 绘制
绘制就是将渲染树中的每个节点转换成屏幕上的实际像素的过程。
总结
浏览器渲染引擎生成页面的 7 个步骤,前面 4 个步骤为 DOM 树的生成过程,后面 3 个步骤是利用 DOM 树和 CSSOM 树来渲染页面的过程。数据的变化过程归结如下:
字节 → 字符 → 令牌 → 树 → 页面