浏览器渲染流程
由于渲染机制过于复杂,所以渲染模块在执行过程中会被划分为很多子阶段,输入的 HTML 经过这些子阶段,最后输出像素。我们把这样的一个处理流程叫做渲染流水线,其大致流程如下图所示:
🐼按照渲染的时间顺序,流水线可分为如下几个子阶段:
- 构建 DOM 树
- 样式计算
- 布局阶段
- 分层
- 绘制
- 分块
- 光栅化
- 合成。
我们可以把渲染流水线当作一个车间流水线,我们只关注输入输出,中间的处理当作黑盒即可;不去关注过程
构建 DOM 树
由于浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM 树。
HTML解析器:接收到HTML字节流时,首先要经过渲染引擎的HTML解析器,将HTML字节流转换成DOM树结构。
构建 DOM 树的输入内容是一个非常简单的 HTML 文件,然后经由 HTML 解析器解析,最终输出树状结构的 DOM。
可以看到,DOM 和 HTML 内容几乎是一样的,但是和 HTML 不同的是,DOM 是保存在内存中树状结构,可以通过 JavaScript 来查询或修改其内容。
样式计算(Recalculate Style)
已经生成 DOM 树了,但是 DOM 节点的样式我们依然不知道,要让 DOM 节点拥有正确的样式,这就需要样式计算了。
样式计算的目的是为了计算出 DOM 节点中每个元素的具体样式,这个阶段大体可分为三步来完成。
1. 把 CSS 转换为浏览器能够理解的结构
和 HTML 文件一样,浏览器也是无法直接理解这些纯文本的 CSS 样式,所以当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。
在控制台中输入 document.styleSheets
,然后就看到如下图所示的结构:
渲染引擎会把获取到的 CSS 文本全部转换为 styleSheets 结构中的数据,并且该结构同时具备了查询和修改功能,这会为后面的样式操作提供基础。
2. 转换样式表中的属性值,使其标准化
将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。
3. 计算出 DOM 树中每个节点的具体样式
样式计算阶段的目的是为了计算出 DOM 节点中每个元素的具体样式,在计算过程中需要遵守 CSS 的继承和层叠两个规则。这个阶段最终输出的内容是每个 DOM 节点的样式,并被保存在 ComputedStyle 的结构内
📚CSS 继承
CSS 继承就是每个 DOM 节点都包含有父节点的样式。
📒层叠
层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法。它在 CSS 处于核心地位,CSS 的全称“层叠样式表”正是强调了这一点
布局阶段
因为我们还不知道 DOM 元素的几何位置信息。那么接下来就需要计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局。
Chrome 在布局阶段需要完成两个任务:创建布局树和布局计算。
1. 创建布局树
- 遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中;
- 而不可见的节点会被布局树忽略掉,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。
2. 布局计算
当Webkit创建RenderObject对象之后,每个对象是不知道自己的位置,大小等信息的,WebKit根据盒子模型来计算他的位置,大小等信息,这个过程就是布局计算
布局计算是一个复杂的过程,我简单的可以总结为:界面是由很多很多的框模型组成的,每个框模型是由元素的类型以及display属性决定的。而不同的Box会参与到不同的格式上下文中(Formatting Context),完成布局的计算。
分层
页面是个二维平面,但是层叠上下文能够让 HTML 元素具有三维概念,这些 HTML 元素按照自身属性的优先级分布在垂直于这个二维平面的 z 轴上。
页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-inde 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。
图层绘制
在完成图层树的构建之后,渲染引擎会对图层树中的每个图层进行绘制;
渲染引擎实现图层的绘制与之类似,会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表,如下图所示
栅格化(raster)操作
绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。
合成和显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
总结
- 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。
- 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
- 创建布局树,并计算元素的布局信息。
- 对布局树进行分层,并生成分层树。
- 为每个图层生成绘制列表,并将其提交到合成线程。
- 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
- 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
- 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。