浏览器首次渲染的几个步骤
- 处理
HTML
标记并构建DOM
树。 - 处理
CSS
标记并构建CSSOM
树。 - 将
DOM
与CSSOM
合并成一个渲染树。渲染树不包括不可见的元素(如:header
下所有,display:none;
的元素,但包括visibility:hidden;
的元素) - Layout : 根据渲染树来布局,以计算每个节点的几何信息。
- Paint : 将各个节点绘制到屏幕上。
优化关键渲染路径就是指最大限度缩短执行上述第 1 步至第 5 步耗费的总时间。 这样一来,就能尽快将内容渲染到屏幕上,此外还能缩短首次渲染后屏幕刷新的时间,即为交互式内容实现更高的刷新率。
检测并分析
工具
- Lighthouse Chrome 扩展程序
Navigation Timing API
:首先了解一下页面加载时的浏览器事件:
几个关键的事件:domInteractive
表示 DOM 准备就绪的时间点。domContentLoaded
一般表示 DOM 和 CSSOM 均准备就绪的时间点。(如果没有阻塞解析器的 JavaScript,则DOMContentLoaded
将在domInteractive
后立即触发。)domComplete
表示网页及其所有子资源都准备就绪的时间点。示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<html>
<head>
<title>Critical Path: Measure</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="style.css" rel="stylesheet">
<script>
function measureCRP() {
var t = window.performance.timing,
interactive = t.domInteractive - t.domLoading,
dcl = t.domContentLoadedEventStart - t.domLoading,
complete = t.domComplete - t.domLoading;
var stats = document.createElement('p');
stats.textContent = 'interactive: ' + interactive + 'ms, ' +
'dcl: ' + dcl + 'ms, complete: ' + complete + 'ms';
document.body.appendChild(stats);
}
</script>
</head>
<body onload="measureCRP()">
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg"></div>
</body>
</html>
实例分析
还是看 google开发文档 ,特别详细清晰。
优化方案
当有 block 的资源时, CRP(Critical Rendering Path) 如图:
所以,我们优化关键渲染路径的常规步骤如下:
- 对关键路径进行分析和特性描述,三个关键渲染路径指标:资源数、字节数、长度。
- 最大限度减少关键资源的数量:删除它们,延迟它们的下载,将它们标记为异步等。
- 优化关键字节数以缩短下载时间(往返次数,
Tcp
会对大文件进行多次往返)。 - 优化其余关键资源的加载顺序:您需要尽早下载所有关键资产,以缩短关键路径长度。
注意: 这些资源并不是 blocked : images
优化 JavaScript 的使用
无论我们使用 <script>
标记还是内联 JavaScript
代码段,您都可以期待两者能够以相同方式工作。 在两种情况下,浏览器都会先暂停构建 DOM
并执行脚本,然后才会处理剩余文档。
由于 JavaScript
还可以操作 CSS
,阻塞解析器的 JavaScript
会强制浏览器等待 CSSOM
构建完成并暂停 DOM
的构建,继而大大延迟首次渲染的时间。 如果是外部 JavaScript
文件,浏览器必须停下来,等待从磁盘、缓存或远程服务器获取脚本,这就可能给关键渲染路径增加数十至数千毫秒的延迟。
所以, JavaScript
资源会阻塞解析器,除非将其标记为 async
或通过专门的 JavaScript
代码段进行添加。
使用异步
JavaScript
资源
向某些script
标记添加异步关键字async
,如分析工具的Js
。这样就不会 blockDOM
的构建,也不会 blockCSSOM
的构建。1
<script src="app.js" async></script>
在之前没有
async
的时候,我们一般将脚本写在onload
事件里,这样可以减少 block 的时间。1
2
3window.onload = function() {
// do something
}延迟解析非必需的
JavaScript
为了最大限度减少浏览器渲染网页的工作量,应延迟任何非必需的脚本(即对构建首次渲染的可见内容无关紧要的脚本)。- 避免运行时间长的
JavaScript
运行时间长的JavaScript
会阻止浏览器构建DOM
、CSSOM
以及渲染网页,所以任何对首次渲染无关紧要的初始化逻辑和功能都应延后执行。如果需要运行较长的初始化序列,请考虑将其拆分为若干阶段,以便浏览器可以间隔处理其他事件。
优化 CSS 的使用
CSS
是构建渲染树的必备元素,首次构建网页时, JavaScript
常常受阻于 CSS。确保将任何非必需的 CSS
都标记为非关键资源(例如打印和其他媒体查询),并应确保尽可能减少关键 CSS
的数量,以及尽可能缩短传送时间。
- 将
CSS
置于文档head
标签内
尽早在HTML
文档内指定所有CSS
资源,以便浏览器尽早发现<link>
标记并尽早发出CSS
请求。 - 避免使用
CSS import
一个样式表可以使用CSS import (@import)
指令从另一样式表文件导入规则。不过,应避免使用这些指令,因为它们会在关键路径中增加往返次数:只有在收到并解析完带有@import
规则的CSS
样式表之后,才会发现导入的CSS
资源。 - 内联阻塞渲染的
CSS
为获得最佳性能,您可能会考虑将关键CSS
直接内联到HTML
文档内。这样做不会增加关键路径中的往返次数,并且如果实现得当,在只有HTML
是阻塞渲染的资源时,可实现“一次往返”关键路径长度。 通过媒体查询减少会 block 首次加载的
CSS
。如:1
2<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="style-print.css" media="print">这样的话, 浏览器仍然会下载两个
CSS
文件,但是首次关键路径渲染不会让style-print.css
block 住, 只有在print
(打印) 时才会渲染style-print.css
,
综合实例
这儿有个题目: udacity/frontend-nanodegree-mobile-portfolio
这是我的作业: jintangWang/frontend-nanodegree-mobile-portfolio
其他
- 迁徙到
http2
webpack
代码拆分- …