理解浏览器关键渲染路径正确姿势

当浏览器接收到服务器返回的HTML响应,并且把像素展示在屏幕上之前,需要经过很多步骤。浏览器渲染的这一系列步骤,我们称之为“关键渲染路径”

如果你对CRP有所了解,那么它对如何提高网站的性能有很大的帮助。CRP有如下6个阶段

  • 构建DOM树
  • 构建CSSOM 树
  • 运行JavaScript
  • 创建渲染树
  • 生成布局
  • 绘制

CRP-Sequence-Copy

1. 构建DOM树

DOM(文档对象模型)树是一个表示HTML页面的对象。它从根元素<html>开始,然后根据每个元素/文本创建节点。嵌套在元素内的元素表示为子节点,每个节点包含该元素的完整属性。比如,<a>元素在其节点上将会存在href属性。

比如接下来的这个HTML页面结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>
<head>
<title>Understanding the Critical Rendering Path</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Understanding the Critical Rendering Path</h1>
</header>
<main>
<h2>Introduction</h2>
<p>Lorem ipsum dolor sit amet</p>
</main>
<footer>
<small>Copyright 2017</small>
</footer>
</body>
</html>

以上页面结构将会被构建成如下DOM树

DOM

HTML的一个好处是它可以部分执行。 内容开始出现在页面上时候,不一定要求页面加载完毕。 但是,对于其他资源,例如(CSS和JavaScript)会阻碍页面的呈现。

2. 构建CSSOM树

CSSOM(CSS对象模型)树是一个表示页面相关样式的对象。它和DOM呈现方式类似,对于每个节点的关联样式,无论明确声明还是隐性继承,都会包含在内。

上面HTML结构中的style.css文件,包含以下样式

1
2
3
4
5
6
7
8
9
10
body { font-size: 18px; }
header { color: plum; }
h1 { font-size: 28px; }
main { color: firebrick; }
h2 { font-size: 20px; }
footer { display: none; }

以上样式将会被构建成如下CSSOM树

CSSOM

CSS因为其“渲染阻塞型资源”特性。这意味着以下的渲染树在没有解析完成之前是不能构建的。不像HTML,CSS因为其继承级联关系,所以是不能被部分执行的。后面定义的样式会覆盖和改变之前定义的样式。如果在页面没有完全渲染之前,我们只用之前定义好的样式,那么页面将展示不正确。所以CSS必须要被完全解析才能进入下一个阶段。

只有被当前设备应用的CSS文件才被视为阻止渲染。 <link rel =“stylesheet”>标签可以接受媒体属性,用于表明在什么设备下加载哪些CSS文件。 例如,如果我们有一个样式表,其媒体属性为orientation:landscape,如果我们正在纵向模式下查看该页面,则该CSS文件不会被视为渲染阻塞。

CSS也可以是“脚本阻塞”。 这是因为JavaScript文件必须等到CSSOM已经构建,然后才能运行。

3. 运行JavaScript

JavaScript 也被视为“渲染阻塞型资源”。那意味着JavaScript会阻塞HTML的解析。

当解析器遇到<script>标签,不管是页面内还是外部引用,HTML渲染就会停止,并且读取JavaScript并执行。这就是为什么,如果我们的JavaScript文件需要引用HTML的元素,它必须放在该文档解析以后。

为了避免JavaScript被解析器阻塞,可以通过设置异步加载async属性来加载它。

4. 创建渲染树

渲染树是DOM和CSSOM的集合。它是页面最终渲染结果的树状结构。这意味着它只会捕获页面可见的内容,比如,如果一个元素CSS样式设置成display: none,那么这个元素是不会被捕获的。

根据上面提到的DOM和CSSOM,他们的渲染树将是这样的

Render-Tree

5. 生成布局

布局是根据视图窗口大小确定的,CSS样式渲染的环境也是依赖它,例如百分比或视图单位。 视图窗口大小由文档头中提供的标签确定,或者如果未提供标签,则使用默认的视口宽度980像素。

例如,最常见的视图窗口值是对应于设备宽度 -

1
2
<meta name="viewport" content="width=device-width,initial-scale=1">

如果用户访问设备上宽度为1000像素,则大小将基于该单位。 一半的视图窗口将是500px,10vw将是100px,等等。

6. 绘制

最后,在绘制步骤中,页面的可见内容可以被转换为要在屏幕上显示的像素。

绘制步骤花费的时间取决于DOM的大小,以及应用的样式。 一些样式需要比其他样式花更多的时间来执行。 例如,复杂的渐变背景图像将需要比简单的实心背景颜色更多的时间。

步骤汇总

要查看页面的关键渲染路径,我们可以在Chrome DevTools中查看它。它位于“Timeline”标签下(在Canary版本下,此版本很快会成为Chrome的稳定版,已重命名为Performance)。

再一起看下上面完整的例子(带<script>标签)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<head>
<title>Understanding the Critical Rendering Path</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Understanding the Critical Rendering Path</h1>
</header>
<main>
<h2>Introduction</h2>
<p>Lorem ipsum dolor sit amet</p>
</main>
<footer>
<small>Copyright 2017</small>
</footer>
<script src="main.js"></script>
</body>
</html>

在页面加载时如果我们查看Event Log,我们将看到

Timeline

  • 发送请求 - 发送获取index.html请求
  • 解析HTML和发送资源请求 - 开始解析HTML和构建DOM。发送获取style.css和main.js的请求
  • 解析样式 - 根据style.css文件创建CSSOM
  • 执行脚本 - 执行main.js
  • 生成布局 - 根据meta里面的视图窗口生成布局
  • 绘制 - 以像素的形式绘制页面

基于以上这些信息,我们就可以决定如何优化关键渲染路径。 我将在后面的文章中介绍一些优化技巧。

原文地址