什么是关键渲染路径(Critical Rendering Path)
按照chrome的解释,关键渲染路径表示在页面初次加载时,哪些资源以高优先级展示。可以使用lighthouse检测,在Critical request chains下,可以看到页面渲染时的关键渲染路径:
减少链的长度,减少资源的下载大小,或推迟下载不必要的资源以提高页面负载。
Navigation Timing API
-
domLoading: 整个流程的开端, 浏览器开始解析(parse)第一个收到的html文档字节. -
domInteractive: 表明浏览器完成了所有html和dom结构的解析,表示Dom准备就绪. -
domContentLoaded: 标志着DOM已经准备完成并且不存在被javaScript阻塞的stylesheets - 意味着页面现在就可以构造渲染树(render tree),表示DOM和CSSOM准备就绪,如果没有js堵塞,会在domInteractive后立即执行.- 许多JavaScript框架在开始执行自己的逻辑之前等待此事件。因此,浏览器会捕获“EventStart”和“EventEnd”时间戳,以便我们跟踪执行时间。
-
domComplete: as the name implies, 所有的流程已经结束,并且所有的页面资源 (图片等.)都被加载完成标志着所有相关资源准备就绪. -
loadEvent: 浏览器就在页面的最后一步,触发onload事件.事件会触发额外的应用程序逻辑
一个计算页面各阶段事件的demo:
<!DOCTYPE html>
<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>
关键路径优化
css方向优化
- 把link放在document的首部:把css link放在首部可以在html parse的过程中,尽快的获取css资源,不被JavaScript阻塞
- 避免使用css imports: 使用@imports会增长关键渲染路径,因为只有在解析到@import时,才会发起对应的css请求
- 内联css,内联css无额外请求,可以将关键css(渲染页面必须的css)用内联的方式引入
- 减少不必要的css: 比如媒体查询的css,可以在媒体条件触发时引入,首屏不需要的css可以不在关键路径引入
js方向优化
无论我们使用<script>标记还是内联JavaScript片段,您都希望两者的行为相同。在这两种情况下,浏览器都会暂停并执行脚本,然后才能处理文档的其余部分。然而,对于外部JavaScript文件,浏览器必须暂停以等待从磁盘、缓存或远程服务器获取脚本,这可能会给关键渲染路径增加数万毫秒的延迟。
默认情况下,所有JavaScript都是解析器阻塞。因为浏览器不知道脚本计划在页面上做什么,所以它假设最坏的情况并阻止解析器。向浏览器发出不需要在引用脚本的确切位置执行脚本的信号,允许浏览器继续构建DOM,并在脚本准备就绪时执行脚本;例如在从缓存或远程服务器获取文件之后。
- 使用async和defer: 如果脚本无需等待页面解析,且无依赖独立运行,那么应使用
async。 如果脚本需要等待页面解析,且依赖于其它脚本,调用这些脚本时应使用defer,将关联的脚本按所需顺序置于 HTML 中。(相关说明) - 避免js长任务
- navigator.sendBeacon (感觉不像是减小首屏资源的方法)