简单流程
- 解析 HTML 文本构建 DOM tree
- 解析 CSS 样式构建 CSSOM tree
- 根据 DOM tree 和 CSSOM tree 构建 Render tree
- 根据 Render tree 信息进行布局处理(Layout)
- 对页面元素进行绘制(Painting)
WebKit main flow

Mozilla Gecko rendering engine main flow

两个主流渲染引擎流程差别不大,这里主要展示 webkit 的流程
解析
解析可以分为两个子流程:词法分析 + 语法分析,这一步把愿文档解析为解析树 Parse Tree

编译
把解析树的内容编译为机器码

树的构建过程
以下边 HTML 为例
<html>
<body>
hello world
</body>
</html>
构建流程可按照 vue 生命周期来理解
- initial mode: 接收到 html 进入 before html 周期
- before html:创建 HTMLHtmlElement 元素 ,并添加到根元素下
- before head:创建 HTMLHeadElement 并添加到 tree
- in head
- after head:创建并添加 HTMLBodyElement
- in body:接收到 hello world,添加到文本节点
- after body
- after after body:html 解析结束标签
- 结束解析

WebKit CSS 解析器
WebKit 使用 Flex 和 Bison 解析器 + 生成器从 CSS 语法文件自动创建解析器。每个 CSS 文件都会被解析为 StyleSheet 对象。每个对象都包含 CSS 规则。CSS 规则对象包含选择器和声明对象,以及与 CSS 语法对应的其他对象

script 脚本与 style sheets 的处理顺序
script
当解析器到达
对页面预解析进行优化
针对 script 脚本加载中会停止文档解析的问题,浏览器引擎进行了优化,支持从主线程分离的预解析技术,脚本在获取和执行过程中,另一个线程会提前解析 HTML 并提前下载需要从网络上加载的资源,实现并行加载提高加载效率
Style sheets
因为 script 脚本执行中可能会需要 css 信息,所以当 Style sheets 还在加载和解析时,FireFox 会阻止脚本,WebKit 在脚本试图访问未被加载的样式时会阻止脚本执行
渲染树
构建 DOM 树的同时,浏览器也会构建渲染树。渲染树的节点顺序按显示顺序来,目的是为了页面绘制按照正确的顺序
WebKit 的绘制类信息如下
class RenderObject{
virtual void layout();
virtual void paint(PaintInfo);
virtual void rect repaintRect();
Node* node; //the DOM node
RenderStyle* style; // the computed style
RenderLayer* containgLayer; //the containing z-index layer
}
render tree 与 DOM tree 的关系
render 节点与 DOM element 是相对应的,但不是一一对应的。不需要展示的 DOM element 不会被添加到 render tree 中,比如 head 标签、display:none 的元素(overflow: hidden 的元素会被添加到 render tree中)
样式计算
生成渲染树需要计算每个渲染对象的视觉属性,这需要计算每个节点的样式数据,不建议把选择器嵌套多层,会增加 css selector 匹配成本消耗更多浏览器性能
paint
paint 阶段,遍历渲染树,调用 paint 方法,以在屏幕上显示内容, paint 使用 ui 组件
动态变化
浏览器为了用户体验在 html 元素有变化时会尽可能少的做页面变更动作,比如改变元素颜色只会导致这个元素的 rePaint,改变元素位置会造成子元素已经被影响的同级元素的 layout 和 repaint,增加 html 元素的字体大小,会从新绘制整个树