让我们回顾一下这个过程中的数据和模块,数据包括网页内容、DOM、内部表示和图像,模块则包括HTML解释器、CSS解释器、JavaScript引擎以及布局和绘图模块。下面深入这些模块并对它们做进一步的细化。

根据数据的流向,这里将渲染过程分成三个阶段,第一个阶段是从网页的URL到构建完DOM树,第二个阶段是从DOM树到构建完WebKit的绘图上下文,第三个阶段是从绘图上下文到生成最终的图像。

为了描述这个过程,下面笔者会将WebKit中的一些细节展示给大家,可能其中一些对读者来说很陌生,不过没关系,笔者会逐步来分析它们。图2-6显示的是将渲染过程分为三个阶段的示意图,主要是针对WebKit中的逻辑来描述的。

wKioL1ZQC4CjxUuYAABYTBAkORs987.png

图2-6描述的是从网页URL到构建完DOM树这个过程,数字表示的是基本顺序,当然也不是严格一致,因为这个过程可能重复并且可能交叉。

具体的过程如下。

1. 当用户输入网页URL的时候,WebKit调用其资源加载器加载该URL对应的网页。

2. 加载器依赖网络模块建立连接,发送请求并接收答复。

3. WebKit接收到各种网页或者资源的数据,其中某些资源可能是同步或异步获取的。

4. 网页被交给HTML解释器转变成一系列的词语(Token)。

5. 解释器根据词语构建节点(Node),形成DOM树。

6. 如果节点是JavaScript代码的话,调用JavaScript引擎解释并执行。

7. JavaScript代码可能会修改DOM树的结构。

8. 如果节点需要依赖其他资源,例如图片、CSS、视频等,调用资源加载器来加载它们,但是它们是异步的,不会阻碍当前DOM树的继续创建;如果是JavaScript资源URL(没有标记异步方式),则需要停止当前DOM树的创建,直到JavaScript的资源加载并被JavaScript引擎执行后才继续DOM树的创建。

在上述的过程中,网页在加载和渲染过程中会发出“DOMConent”事件和DOM的“onload”事件,分别在DOM树构建完之后,以及DOM树建完并且网页所依赖的资源都加载完之后发生,因为某些资源的加载并不会阻碍DOM树的创建,所以这两个事件多数时候不是同时发生的。

接下来就是WebKit利用CSS和DOM树构建RenderObject树直到绘图上下文,如图2-7所示的过程。

这一阶段的具体过程如下。

1. CSS文件被CSS解释器解释成内部表示结构。

2. CSS解释器工作完之后,在DOM树上附加解释后的样式信息,这就是RenderObject树。


wKioL1ZQC6jx-tzpAABL3R1sXME085.png

3. RenderObject节点在创建的同时,WebKit会根据网页的层次结构创建RenderLayer树,同时构建一个虚拟的绘图上下文。其实这中间还有复杂的内部过程,具体在后面专门的章节做详细介绍。

RenderObject树的建立并不表示DOM树会被销毁,事实上,上述图中的四个内部表示结构一直存在,直到网页被销毁,因为它们对于网页的渲染起了非常大的作用。

最后就是根据绘图上下文来生成最终的图像,这一过程主要依赖2D和3D图形库,如图2-8所示。

wKioL1ZQC8fS9n-dAABEK-AKxp4724.png

图中这一阶段对应的具体过程如下。

1. 绘图上下文是一个与平台无关的抽象类,它将每个绘图操作桥接到不同的具体实现类,也就是绘图具体实现类。

2. 绘图实现类也可能有简单的实现,也可能有复杂的实现。在Chromium中,它的实现相当复杂,需要Chromium的合成器来完成复杂的多进程和GPU加速机制,这在后面会涉及。

3. 绘图实现类将2D图形库或者3D图形库绘制的结果保存下来,交给浏览器来同浏览器界面一起显示。

这一过程实际上可能不像图中描述的那么简单,现代浏览器为了绘图上的高效性和安全性,可能会在这一过程中引入复杂的机制。而且,绘图也从之前单纯的软件渲染,到现在的GPU硬件渲染、混合渲染模型等方式,这些同样会以单独的章节加以剖析。

上面介绍的是一个完整的渲染过程。现代网页很多是动态网页,这意味着在渲染完成之后,由于网页的动画或者用户的交互,浏览器其实一直在不停地重复执行渲染过程。