优化关键渲染路径

上个月,我写了一篇文章介绍什么是“关键渲染路径”,其实目的是为了给这篇文章作一个铺垫,本文将谈谈如何优化关键渲染路径(本文将假设您已经阅读过《关键渲染路径》这篇文章或已经懂得了什么是“关键渲染路径”)。php

优化关键渲染路径能够提高网页的渲染速度,从而获得一个更好的用户体验。css

如何优化关键渲染路径?

优化关键渲染路径有不少种方法与状况,不一样状况下优化方式也各不相同,初步看起来这些优化方法五花八门,知识很是的零散。html

但在这些看似零散的知识中,咱们会发现一些规律,将这些规律总结起来后,能够得出一个结论:到目前为止,只有三种因素能够影响关键渲染路径的耗时。而全部的优化方式,都是在尽量的针对这三种因素进行优化。git

这三种因素分别是:github

  • 关键资源的数量
  • 关键路径的长度
  • 关键字节的数量

切记,很是重要,全部优化关键渲染路径的方法,都是在优化以上三种因素。由于只有这三种因素能够影响关键渲染路径。浏览器

关键资源指的是那些能够阻塞页面首次渲染的资源。例如JavaScript、CSS都是能够阻塞关键渲染路径的资源,这些资源就属于“关键资源”。关键资源的数量越少,浏览器处理渲染的工做量就越少,同时CPU及其余资源的占用也越少。缓存

关键路径中的每一步耗时越长,因为阻塞会致使渲染路径的总体耗时变长。关键路径的长度指的是关键渲染路径的总耗时。关键渲染路径的长度会受到不少因素的影响。例如:关键资源的网络状况、关键资源的数量、关键资源的字节大小、关键资源的依赖关系等。网络

关键字节的数量指的是关键资源的字节大小,浏览器要下载的资源字节越小,则下载速度与处理资源的速度都会更快。一般不少优化方法都是针对关键字节的数量进行优化。例如:压缩。app

优化DOM

在关键渲染路径中,构建渲染树(Render Tree)的第一步是构建DOM,因此咱们先讨论如何让构建DOM的速度变得更快。dom

HTML文件的尺寸应该尽量的小,目的是为了让客户端尽量早的接收到完整的HTML。一般HTML中有不少冗余的字符,例如:JS注释、CSS注释、HTML注释、空格、换行。更糟糕的状况是我见过不少生产环境中的HTML里面包含了不少废弃代码,这多是由于随着时间的推移,项目愈来愈大,因为种种缘由从历史遗留下来的问题,不过无论怎么说,这都是很糟糕的。对于生产环境的HTML来讲,应该删除一切无用的代码,尽量保证HTML文件精简。

总结起来有三种方式能够优化HTML:缩小文件的尺寸(Minify)使用gzip压缩(Compress)使用缓存(HTTP Cache)

缩小文件的尺寸(Minify)会删除注释、空格与换行等无用的文本。

本质上,优化DOM实际上是在尽量的减少关键路径的长度与关键字节的数量

优化CSSOM

与优化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开发者工具的性能面板捕获后的性能图以下:

阻塞的CSS资源性能捕获图

从上图中的首次绘制(First Paint)时间是在1200ms的位置,能够看到这个时间是浏览器加载CSS完毕后,并且能够看到Network栏中CSS显示Highest表明高优先级。

添加了媒体查询语句后,捕获出来的性能图以下:

非阻塞的CSS资源性能捕获图

首次绘制时间在不到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从两个方面解决了性能问题:

  1. 减小关键资源的数量(将全部与首屏渲染无关的CSS使用异步非阻塞加载)
  2. 减小关键路径的长度(将首屏渲染须要的CSS直接内嵌到head标签中,移除了网络请求的时间)。

避免使用@import

你们应该都知道要避免使用@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面板捕获出的结果以下图所示:

使用link标签加载样式

从图中用红色方框圈出来的位置能够看出两个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开发者工具再次捕获出的结果以下图所示:

使用@import加载CSS

能够看到两个CSS变成了串行加载,前一个CSS加载完后再去下载使用@import导入的CSS资源。这无疑会致使加载资源的总时间变长。从上图能够看出,首次绘制时间等于两个CSS资源加载时间的总和。

因此避免使用@import是为了下降关键路径的长度。

异步JavaScript

全部文本资源都应该让文件尽量的小,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资源

从捕获出的结果能够看到,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

从图中能够看到,JS加载完后再也不须要等待CSS资源,而且也再也不阻塞DOM的构建,最终domcontentloaded事件在50ms与100ms之间触发。与以前相比,domcontentloaded事件触发时间提早了300ms。

能够看到,在关键渲染路径中优化JavaScript,目的是为了减小关键资源的数量

总结

该篇文章详细介绍了如何优化关键渲染路径。

关键渲染路径是浏览器将HTML,CSS,JavaScript转换为屏幕上所呈现的实际像素的具体步骤。而优化关键渲染路径能够提升网页的呈现速度,也就是首屏渲染优化。

你会发现,咱们介绍的内容都是如何优化DOM,CSSOM以及JavaScript,由于一般在关键渲染路径中,这些步骤的性能最差。这些步骤是致使首屏渲染速度慢的主要缘由。

相关文章
相关标签/搜索