Skip to main content

html 渲染流程

总体步骤

渲染流水线: 构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成

构建DOM树

构建 DOM 树的输入内容是一个非常简单的 HTML 文件,然后经由 HTML 解析器解析,最终输出树状结构的 DOM

样式计算

1. 把 CSS 转换为浏览器能够理解的结构

css来源有三种:

  • 通过 link 引用的外部 CSS 文件
  • <style>标记内的css
  • 元素style属性内嵌css

渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets

2. 转换样式表中的属性值,使其标准化

需要将所有值转换为渲染引擎容易理解的、标准化的计算值

tips: 2em 被解析成了 32px,red 被解析成了 rgb(255,0,0),bold 被解析成了 700

3. 计算出 DOM 树中每个节点的具体样式

涉及到 CSS 的继承规则和层叠规则 样式计算阶段的目的是为了计算出 DOM 节点中每个元素的具体样式,在计算过程中需要遵守 CSS 的继承和层叠两个规则。这个阶段最终输出的内容是每个 DOM 节点的样式,并被保存在 ComputedStyle 的结构内。

布局阶段

计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局; Chrome 在布局阶段需要完成两个任务:创建布局树和布局计算。

1. 创建布局树

构建一棵只包含可见元素布局树。 为了构建布局树,浏览器大体上完成了下面这些工作:

  • 遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中;
  • 不可见的节点会被布局树忽略掉;

2. 布局计算

在执行布局操作的时候,会把布局运算的结果重新写回布局树中,所以布局树既是输入内容也是输出内容,这是布局阶段一个不合理的地方,因为在布局阶段并没有清晰地将输入内容和输出内容区分开来。针对这个问题,Chrome 团队正在重构布局代码,下一代布局系统叫 LayoutNG,试图更清晰地分离输入和输出,从而让新设计的布局算法更加简单。

分层

渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)

  1. 拥有层叠上下文属性的元素会被提升为单独的一层。(明确定位属性的元素、定义透明属性的元素、使用 CSS 滤镜的元素等,都拥有层叠上下文属性。)
  2. 需要剪裁(clip)的地方也会被创建为图层。

图层绘制

一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表; 图层绘制阶段,输出的内容就是这些待绘制列表。

栅格化(raster)操作

绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。 合成线程会将图层划分为图块(tile),这些图块的大小通常是 256x256 或者 512x512 合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。而图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的,栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化

合成和显示

一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。

总结

  • 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。
  • 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
  • 创建布局树,并计算元素的布局信息。
  • 对布局树进行分层,并生成分层树。
  • 为每个图层生成绘制列表,并将其提交到合成线程。
  • 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  • 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
  • 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。