上个月,我写了一篇文章介绍什么是“关键渲染路径”,其实目的是为了给这篇文章作一个铺垫,本文将谈谈如何优化关键渲染路径(本文将假设您已经阅读过《关键渲染路径》这篇文章或已经懂得了什么是“关键渲染路径”)。php
优化关键渲染路径能够提高网页的渲染速度,从而获得一个更好的用户体验。css
优化关键渲染路径有不少种方法与状况,不一样状况下优化方式也各不相同,初步看起来这些优化方法五花八门,知识很是的零散。html
但在这些看似零散的知识中,咱们会发现一些规律,将这些规律总结起来后,能够得出一个结论:到目前为止,只有三种因素能够影响关键渲染路径的耗时。而全部的优化方式,都是在尽量的针对这三种因素进行优化。git
这三种因素分别是:github
切记,很是重要,全部优化关键渲染路径的方法,都是在优化以上三种因素。由于只有这三种因素能够影响关键渲染路径。浏览器
关键资源指的是那些能够阻塞页面首次渲染的资源。例如JavaScript、CSS都是能够阻塞关键渲染路径的资源,这些资源就属于“关键资源”。关键资源的数量越少,浏览器处理渲染的工做量就越少,同时CPU及其余资源的占用也越少。缓存
关键路径中的每一步耗时越长,因为阻塞会致使渲染路径的总体耗时变长。关键路径的长度指的是关键渲染路径的总耗时。关键渲染路径的长度会受到不少因素的影响。例如:关键资源的网络状况、关键资源的数量、关键资源的字节大小、关键资源的依赖关系等。网络
关键字节的数量指的是关键资源的字节大小,浏览器要下载的资源字节越小,则下载速度与处理资源的速度都会更快。一般不少优化方法都是针对关键字节的数量进行优化。例如:压缩。app
在关键渲染路径中,构建渲染树(Render Tree)的第一步是构建DOM,因此咱们先讨论如何让构建DOM的速度变得更快。dom
HTML文件的尺寸应该尽量的小,目的是为了让客户端尽量早的接收到完整的HTML。一般HTML中有不少冗余的字符,例如:JS注释、CSS注释、HTML注释、空格、换行。更糟糕的状况是我见过不少生产环境中的HTML里面包含了不少废弃代码,这多是由于随着时间的推移,项目愈来愈大,因为种种缘由从历史遗留下来的问题,不过无论怎么说,这都是很糟糕的。对于生产环境的HTML来讲,应该删除一切无用的代码,尽量保证HTML文件精简。
总结起来有三种方式能够优化HTML:缩小文件的尺寸(Minify)、使用gzip压缩(Compress)、使用缓存(HTTP Cache)。
缩小文件的尺寸(Minify)会删除注释、空格与换行等无用的文本。
本质上,优化DOM实际上是在尽量的减少关键路径的长度与关键字节的数量。
与优化DOM相似,CSS文件也须要让文件尽量的小,或者说全部文本资源都须要。CSS文件应该删除未使用的样式、缩小文件的尺寸(Minify)、使用gzip压缩(Compress)、使用缓存(HTTP Cache)。
除了上面提到的优化策略,CSS还有一个能够影响性能的因素是:CSS会阻塞关键渲染路径。
CSS是关键资源,它会阻塞关键渲染路径也并不奇怪,但一般并非全部的CSS资源都那么的『关键』。
举个例子:一些响应式CSS只在屏幕宽度符合条件时才会生效,还有一些CSS只在打印页面时才生效。这些CSS在不符合条件时,是不会生效的,因此咱们为何要让浏览器等待咱们并不须要的CSS资源呢?
针对这种状况,咱们应该让这些非关键的CSS资源不阻塞渲染。
实现这一目的很是简单,咱们只须要将不阻塞渲染的CSS移动到单独的文件里。例如咱们将打印相关的CSS移动到print.css
,而后咱们在HTML中引入CSS时,添加媒体查询属性print
,代码以下:
<link href="print.css" rel="stylesheet" media="print">
复制代码
上面代码添加了media="print"
属性,因此上面CSS资源仅用于打印。添加了媒体查询属性后,浏览器依然会下载该资源,但若是条件不符合,那么它就再也不阻塞渲染,也就是变成了非阻塞的CSS。
咱们能够写个DEMO测试一下:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demos</title>
<link rel="stylesheet" href="https://static.xx.fbcdn.net/rsrc.php/v3/y6/l/1,cross/9Ia-Y9BtgQu.css">
</head>
<body>
Hello
</body>
</html>
复制代码
上面代码使用Chrome开发者工具的性能面板捕获后的性能图以下:
从上图中的首次绘制(First Paint)时间是在1200ms的位置,能够看到这个时间是浏览器加载CSS完毕后,并且能够看到Network栏中CSS显示Highest
表明高优先级。
添加了媒体查询语句后,捕获出来的性能图以下:
首次绘制时间在不到100ms的位置,和domcontentloaded事件差很少的时间触发。同时CSS资源变成了Lowest
,表示低优先级。
能够看到,浏览器依然会下载该CSS资源,但它再也不阻塞渲染。
上面提供的方法是针对那些不须要生效的CSS资源,若是CSS资源须要在当前页面生效,只是不须要在首屏渲染时生效,那么为了更快的首屏渲染速度,咱们能够将这些CSS也设置成非关键资源。只是咱们须要一些比较hack的方式来实现这个需求:
<link href="style.css" rel="stylesheet" media="print" onload="this.media='all'">
复制代码
上面代码先把媒体查询属性设置成print
,将这个资源设置成非阻塞的资源。而后等这个资源加载完毕后再将媒体查询属性设置成all
让它当即对当前页面生效。
经过这样的方式,咱们既可让这个资源不阻塞关键渲染路径,还可让它加载完毕后对当前页面生效。
相似的方案有不少,代码以下:
<link rel="preload" href="style.css" as="style" onload="this.rel='stylesheet'">
<link rel="alternate stylesheet" href="style.css" onload="this.rel='stylesheet'">
复制代码
上面两种方式都能实现一样的效果。
关于CSS的加载有这么多门道,到底怎样才是最佳实践?答案是:Critical CSS。
Critical CSS的意思是:把首屏渲染须要使用的CSS经过style标签内嵌到head标签中,其他CSS资源使用异步的方式非阻塞加载。
CSS资源在构建渲染树时,会阻塞JavaScript,因此咱们应该保证全部与首屏渲染无关的CSS资源都应该被标记为非关键资源。
因此Critical CSS从两个方面解决了性能问题:
你们应该都知道要避免使用@import
加载CSS,实际工做中咱们也不会这样去加载CSS,但这究竟是为何呢?
这是由于使用@import
加载CSS会增长额外的关键路径长度。举个例子:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demos</title>
<link rel="stylesheet" href="http://127.0.0.1:8887/style.css">
<link rel="stylesheet" href="https://lib.baomitu.com/CSS-Mint/2.0.6/css-mint.min.css">
</head>
<body>
<div class="cm-alert">Default alert</div>
</body>
</html>
复制代码
上面这段代码使用link
标签加载了两个CSS资源。这两个CSS资源是并行下载的。咱们使用Chrome开发者工具的Performance面板捕获出的结果以下图所示:
从图中用红色方框圈出来的位置能够看出两个CSS是并行加载的,首次绘制时间取决于CSS加载时间较长的资源加载时间。
如今咱们改成使用@import
加载资源,代码以下:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demos</title>
<link rel="stylesheet" href="http://127.0.0.1:8887/style.css">
</head>
<body>
<div class="cm-alert">Default alert</div>
</body>
</html>
复制代码
/* style.css */
@import url('https://lib.baomitu.com/CSS-Mint/2.0.6/css-mint.min.css');
body{background:red;}
复制代码
代码中使用link标签加载一个CSS,而后在CSS文件中使用@import
加载另外一个CSS。使用Chrome开发者工具再次捕获出的结果以下图所示:
能够看到两个CSS变成了串行加载,前一个CSS加载完后再去下载使用@import
导入的CSS资源。这无疑会致使加载资源的总时间变长。从上图能够看出,首次绘制时间等于两个CSS资源加载时间的总和。
因此避免使用@import
是为了下降关键路径的长度。
全部文本资源都应该让文件尽量的小,JavaScript也不例外,它也须要删除未使用的代码、缩小文件的尺寸(Minify)、使用gzip压缩(Compress)、使用缓存(HTTP Cache)。
与CSS资源类似,JavaScript资源也是关键资源,JavaScript资源会阻塞DOM的构建。而且JavaScript会被CSS文件所阻塞。为了不阻塞,能够为script
标签添加async
属性。
咱们举个例子:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demos</title>
<link rel="stylesheet" href="https://static.xx.fbcdn.net/rsrc.php/v3/y6/l/1,cross/9Ia-Y9BtgQu.css">
</head>
<body>
<p class='_159h'>aa</p>
<script src="http://qiniu.bkt.demos.so/static/js/app.53df42d5b7a0dbf52386.js"></script>
</body>
</html>
复制代码
上面这段代码,分别加载了CSS资源和JavaScript资源,咱们使用Chrome开发者工具的Performance面板捕获出的结果以下图所示:
从捕获出的结果能够看到,JS资源加载完毕后,须要等待CSS资源加载完并构建出CSSOM以后才会执行JS,而且JS会将DOM阻塞,因此最终domcontentloaded
事件在350ms与400ms之间触发。
咱们将script标签添加async
属性:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demos</title>
<link rel="stylesheet" href="https://static.xx.fbcdn.net/rsrc.php/v3/y6/l/1,cross/9Ia-Y9BtgQu.css">
</head>
<body>
<p class='_159h'>aa</p>
<script async src="http://qiniu.bkt.demos.so/static/js/app.53df42d5b7a0dbf52386.js"></script>
</body>
</html>
复制代码
使用Chrome开发者工具捕获出的结果以下图所示:
从图中能够看到,JS加载完后再也不须要等待CSS资源,而且也再也不阻塞DOM的构建,最终domcontentloaded
事件在50ms与100ms之间触发。与以前相比,domcontentloaded
事件触发时间提早了300ms。
能够看到,在关键渲染路径中优化JavaScript,目的是为了减小关键资源的数量。
该篇文章详细介绍了如何优化关键渲染路径。
关键渲染路径是浏览器将HTML,CSS,JavaScript转换为屏幕上所呈现的实际像素的具体步骤。而优化关键渲染路径能够提升网页的呈现速度,也就是首屏渲染优化。
你会发现,咱们介绍的内容都是如何优化DOM,CSSOM以及JavaScript,由于一般在关键渲染路径中,这些步骤的性能最差。这些步骤是致使首屏渲染速度慢的主要缘由。