发布时间:2014年3月31日
CSSOM 和 DOM 树被组合成渲染树,然后用于计算每个可见元素的布局,并作为绘制过程的输入,将像素渲染到屏幕上。优化这些步骤中的每一步对于实现最佳渲染性能至关重要。
在上一节关于构建对象模型的内容中,我们基于 HTML 和 CSS 输入构建了 DOM 和 CSSOM 树。然而,这两者都是独立的对象,它们捕获了文档的不同方面:一个描述内容,另一个描述需要应用于文档的样式规则。我们如何合并这两者,并让浏览器在屏幕上渲染像素呢?
总结
- DOM 和 CSSOM 树结合形成渲染树。
- 渲染树仅包含渲染页面所需的节点。
- 布局计算每个对象的精确位置和大小。
- 最后一步是绘制,它接收最终的渲染树并将像素渲染到屏幕上。
首先,浏览器将 DOM 和 CSSOM 组合成“渲染树”,它捕获页面上所有可见的 DOM 内容以及每个节点的 CSSOM 样式信息。
为了构建渲染树,浏览器大致执行以下操作
从 DOM 树的根节点开始,遍历每个可见节点。
- 某些节点是不可见的(例如,script 标签、meta 标签等),并且被省略,因为它们不反映在渲染输出中。
- 某些节点使用 CSS 隐藏,也会从渲染树中省略;例如,上面的示例中的 span 节点从渲染树中消失了,因为我们有一个显式规则将 span 节点的 "display" 属性设置为 "none"。
对于每个可见节点,找到适当的匹配 CSSOM 规则并应用它们。
发出具有内容及其计算样式的可见节点。
最终输出是一个渲染树,其中包含屏幕上所有可见内容的内容和样式信息。有了渲染树,我们就可以继续进行“布局”阶段。
到目前为止,我们已经计算出哪些节点应该是可见的以及它们的计算样式,但我们尚未计算它们在设备视口中的精确位置和大小——这就是“布局”阶段,也称为“重排”。
为了确定页面上每个对象的精确大小和位置,浏览器从渲染树的根节点开始遍历它。考虑以下示例
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Critial Path: Hello world!</title>
</head>
<body>
<div style="width: 50%">
<div style="width: 50%">Hello world!</div>
</div>
</body>
</html>
前一个示例的 <body>
包含两个嵌套的 <div>
:第一个(父)<div>
将节点的显示大小设置为视口宽度的 50%
,第二个 <div>
——由父级包含——将其 width
设置为其父级的 50%
;即视口宽度的 25%。
布局过程的输出是一个“盒模型”,它精确地捕获了视口中每个元素的精确位置和大小:所有相对测量值都转换为屏幕上的绝对像素。
最后,既然我们知道了哪些节点是可见的,以及它们的计算样式和几何形状,我们可以将此信息传递到最后阶段,该阶段将渲染树中的每个节点转换为屏幕上的实际像素。此步骤通常称为“绘制”或“栅格化”。
这可能需要一些时间,因为浏览器必须做相当多的工作。但是,Chrome DevTools 可以提供对前面描述的所有三个阶段的一些深入了解。检查我们最初的“hello world”示例的布局阶段
- “布局”事件捕获时间轴中的渲染树构建、位置和大小计算。
- 当布局完成时,浏览器会发出“绘制设置”和“绘制”事件,这些事件将渲染树转换为屏幕上的像素。
执行渲染树构建、布局和绘制所需的时间因文档大小、应用的样式以及运行它的设备而异:文档越大,浏览器的工作量就越大;样式越复杂,绘制所需的时间也越长(例如,绘制纯色是“廉价的”,而计算和渲染阴影是“昂贵的”)。
页面最终在视口中可见
以下是浏览器步骤的快速回顾
- 处理 HTML 标记并构建 DOM 树。
- 处理 CSS 标记并构建 CSSOM 树。
- 将 DOM 和 CSSOM 组合成渲染树。
- 在渲染树上运行布局以计算每个节点的几何形状。
- 将各个节点绘制到屏幕上。
演示页面可能看起来很简单,但它需要在浏览器端进行相当多的工作。如果 DOM 或 CSSOM 中的任何一个被修改,您将不得不重复该过程,以找出哪些像素需要在屏幕上重新渲染。
优化关键渲染路径 是最大限度地减少在上述序列的步骤 1 到 5 中花费的总时间的过程。 这样做可以尽快将内容渲染到屏幕上,并减少初始渲染后屏幕更新之间的时间量;也就是说,为交互式内容实现更高的刷新率。