浏览器开始渲染网页之前,需要构建 “DOM” 和 “CSSOM” 两棵树。
简述
- 字节 → 字符 → 符号 → 节点 → 对象模型
- HTML 将会被翻译成 “文档对象模型(Document Object Model,DOM)”;CSS 将会被翻译成 “样式表对象模型(CSS Object Model,CSSOM)”
- CSSOM 和 DOM 二者无关联,是两颗独立的树
- Chrome 的开发者工具可以让我们很轻松的地捕捉并检查构建 DOM 和 CSSOM 时的花销。
文档对象模型(DOM)
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="style.css" rel="stylesheet">
<title>Critical Path</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div>![](awesome-photo.jpg)</div>
</body>
</html>
让我们从一个简单的例子开始:一个简单的 HTML 文件,里面包含了一行文字和一张图片。浏览器是如何渲染这个页面的?
- 转换:浏览器从网络或者磁盘中读取网页的字节内容,并且根据标注的编码(比如 UTF-8)将这些内容转换成字符。
-
标记化(Tokenizing):浏览器将会根据HTML 的标准把上一步的结果转换成 “标记(Token)”。比如
<html>
或者<body>
这种被尖括号包住的 "标记(Token)" 均有特殊的意义 - 词法分析:上一步的结果将会被转换成对象(Object),其中包含了节点的各种属性和规则。
- 生成 DOM 树:最后,由于在 HTML 中每一个标签之间都有联系(比如子父级关系),上一步创建的对象将会形成一个树状的结构,如下图:
本过程最终的产物是文档对象模型(DOM),浏览器将会用这棵树来进行页面的相关操作。
以上过程总是会发生在浏览器解析 HTML 时——将字节转换为字符,将字符转换为标记,将标记转换成节点(nodes),最后将 DOM 树创建出来。整个过程可能需要一些时间,尤其是在页面比较大的情况下。
打开 Chrome,并在页面加载的时候开始一个性能分析,观察时间线,你会看到解析 DOM 的花费时间——在这个例子中,从 HTML 到 DOM 树大约需要 5ms(译者这里是0.8ms)。页面内容越多,该时间将会被显著延长。由此可见,如果我们想让自己的网页有平滑的体验,那么这将是一个性能瓶颈。
DOM 只是描述了文档节点之间的结构和属性,并没有告诉我们每个节点应该如何渲染——这就是 CSSOM 的事情了。
CSS Object Model (CSSOM)
当浏览器构建 示例页面 的 DOM 时,它在 页面的 head 区域发现了一个 link 标签,该标签引用了一个外部的 CSS:style.css。
浏览器内心想:
我必须要这个文件才能开始页面的渲染呀!
于是它发送了一个事件,用于请求style.css。返回的内容是这样的:
body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }
我们也可以将 CSS 写到 HTML 的 style 标签中,但是将 CSS 和 HTML 分开有助于我们将设计和开发的工作分开:设计师专注于写 CSS,开发者专注于 HTML。
CSS 的解析过程和 HTML 的解析过程类似,同样是 字节 → 字符 → 符号 → 节点 → 对象模型,如下图:
经过以上过程的解析,CSS将会被解析成下图的样子:
为什么 CSSOM 也会有树状结构呢?当 CSSOM 生成完毕时,浏览器将会从上层的元素(比如body)开始应用 CSS。比如如果在 body 上设置了 font-size:8px,那么所有的 body 节点下所有子元素的字体大小均会变为 8px。由此开始,浏览器将会根据 CSSOM 从上到下渲染每一层的元素,这就是我们说的 “层叠样式表(Cascading Style Sheets)”。
上图的 CSSOM 将会这样渲染:所有 body 下面的 span 标签将会有红色的字体,并且字体为
16px(从 body 继承)。然而,如果 span 是 p 的子元素,它应该会被隐藏(display: none)。
需要注意的是,上图的这棵树并不完整。每一个浏览器都会有一个 “浏览器自定义样式(user agent styles)”,也就是说如果我们不提供自定义样式的话,我们的页面将会按照浏览器提供的样式来渲染。
在 Chrome 的开发者工具中,我们可以找到一个名为 “Recalculate Style” 的事件。和 DOM 不同的是,它并没有一个叫 “Parse CSS” 的事件。CSS 解析,CSSOM 构建,递归计算 “计算属性(Computed Style)” 的所有的时间消耗全部在一个 “Recalculate Style” 事件中。
我们这点 CSS 的计算消耗了大约0.6ms(译者 0.7ms),不多,但毕竟还是需要消耗时间。但是你可能会发现,那受影响的8个元素(Elements Affected)是哪来的?不是说 DOM 和 CSSOM 是分离的吗?那下篇文章,我们将会介绍一颗将 DOM 和 CSSOM 关联到一起的树:渲染树。
译者注:
我们可以在 Parse HTML 中发现一个事件,用于请求 style.css。如下图
注意此时请求还未发出,只是触发了一个事件。
原文的 Chrome 开发者工具版本较老,译者更新了部分图片,请自行参照最新版本的 Chrome 开发者工具文档 来进行操作。