在页面的加载过程中,CSSOM树和DOM树会被合并成一棵渲染树,用于计算每个可视元素的布局。同时,它也会作为绘制过程的输入参数,用于绘制屏幕上的每个像素点。优化其中的每一步都对优化页面的渲染性能至关重要。
在前面的章节中,我们介绍了对象模型的构建,也就是根据加载的HTML和CSS代码构建DOM树和CSSOM树。然而它们本质上是两个互相独立的东西,各自描述了页面文档的一个不同方面:前者描述了内容,后者描述了应用于内容的样式规则。那么浏览器是如何将两者结合起来并在屏幕上画出每个像素点的呢?
学习重点
对浏览器而言,第一步便是将DOM树和CSSOM树合并成一棵渲染树。渲染树既包含了页面上所有的可视DOM节点,又包含了CSSOM中每个节点的样式信息。
为了创建渲染树,浏览器大致需要如下的步骤:
简要提示一下,CSS中“visibility:hidden”和“display:none”是不同的。前者将元素隐藏起来,但是隐藏元素仍然会在页面最终的布局中占据相应的空间(其实就是一块空白)。然而后者会直接将元素从渲染树中删除,不仅不可见,也不属于最终布局的一部分。
最终的渲染树既包含了所有可视的内容,又包含了相应的样式信息。快要大功告成了!有了这棵渲染树,我们就能进入下一步,布局。
在此之前,我们已经计算了什么节点是可视的以及它们对应的样式是什么,但是我们还没有计算它们在当前设备中准确的位置和尺寸。这正是布局阶段要做的的工作,该阶段在英语中也被称为“回流”(reflow)。
为了算出每个对象的准确大小和位置,浏览器从渲染树的根节点开始遍历,计算页面中每个对象的几何样式。下面就让我们看一个简单的例子:
1
2
3
4
5
6
7
8
9
10
11
|
<
html
>
<
head
>
<
meta
name
=
"viewport"
content
=
"width=device-width,initial-scale=1.0"
>
<
title
>Critial Path: Hello world!</
title
>
</
head
>
<
body
>
<
div
style
=
"width: 50%"
>
<
div
style
=
"width: 50%"
>Hello world!</
div
>
</
div
>
</
body
>
</
html
>
|
上面的页面中包含两个嵌套的div元素:第一个(父元素)设定了显示宽度为窗口的50%,第二个元素(子元素)设定了宽度为父元素的50%,即窗口宽度的25%。
布局阶段的输出结果称为 “盒模型”(box model)。盒模型精确表达了窗口中每个元素的位置和大小,而且所有的相对的度量单位都被转化成了屏幕上的绝对像素位置。
在最后阶段,已经知道了哪些节点是可视的、它们的样式和它们的几何外观,我们终于能够将这些信息渲染为屏幕上每个真实的像素点了。这个阶段称为“绘制”,或者“格栅化”(rasterizing)。
到这里,各位读者理解了没?上述每一步中浏览器都需要进行大量的计算工作,也就是说需要消耗相当的时间。幸好,Chrome DevTools工具能够帮我们深入了解上述每一步。让我们一起看一看之前例子中“hello world”页面在布局阶段的数据:
渲染树的构建、页面布局以及绘制所花费的时间取决于页面的尺寸、样式的规则以及运行的设备。页面越大,浏览器的工作就越多;规则越复杂,就需要更多的时间来绘制(绘制单色比较方便,相比之下阴影的计算和渲染就麻烦的多了)。
一旦上述步骤都完成了,我们的页面就会呈现在屏幕上。
让我们来快速回顾一下浏览器处理过程中的每一个步骤:
我们的例子页面也许看起来很简单,但是也需要浏览器做一系列计算来呈现出来。读者们觉得当我们修改DOM和CSSOM的时候会发生什么呢?浏览器需要重复上述步骤来确定哪些像素点需要重新进行渲染。
优化关键渲染路径就是减少上述步骤1至步骤5所花费的总时间。如此,我们才能尽快将内容渲染到屏幕上,缩短初次加载之后每次刷新的时间间隔,以更快的速度展现页面中的交互式内容。