浏览器渲染流程

617 阅读3分钟

简单流程

  1. 解析 HTML 文本构建 DOM tree
  2. 解析 CSS 样式构建 CSSOM tree
  3. 根据 DOM tree 和 CSSOM tree 构建 Render tree
  4. 根据 Render tree 信息进行布局处理(Layout)
  5. 对页面元素进行绘制(Painting)

WebKit main flow

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a1e533842e9b4981b0e2f0fab34a92d1~tplv-k3u1fbpfcp-zoom-1.image

Mozilla Gecko rendering engine main flow

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ce5c1ac69899458cac9dedf0deb66e1a~tplv-k3u1fbpfcp-zoom-1.image

两个主流渲染引擎流程差别不大,这里主要展示 webkit 的流程

解析

解析可以分为两个子流程:词法分析 + 语法分析,这一步把愿文档解析为解析树 Parse Tree

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a8c99fbbf67245c6913625997e485b29~tplv-k3u1fbpfcp-zoom-1.image

编译

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

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dea7119d172848bdbaddf8e8a14976fc~tplv-k3u1fbpfcp-zoom-1.image

树的构建过程

以下边 HTML 为例

<html>
	<body>
		hello world
	</body>
</html>

构建流程可按照 vue 生命周期来理解

  1. initial mode: 接收到 html 进入 before html 周期
  2. before html:创建 HTMLHtmlElement 元素 ,并添加到根元素下
  3. before head:创建 HTMLHeadElement 并添加到 tree
  4. in head
  5. after head:创建并添加 HTMLBodyElement
  6. in body:接收到 hello world,添加到文本节点
  7. after body
  8. after after body:html 解析结束标签
  9. 结束解析

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/14da86e2e19c4b168593c5332464ceb0~tplv-k3u1fbpfcp-zoom-1.image

WebKit CSS 解析器

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

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/09bccc6d1fd84f6e94095ce988c4d6c0~tplv-k3u1fbpfcp-zoom-1.image

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 元素的字体大小,会从新绘制整个树