总结于: Critical Rendering Path
从收到 HTML、CSS 和 JavaScript 字节到对其进行必需的处理,从而将它们转变成渲染的像素这一过程当中有一些中间步骤,优化性能其实就是了解这些步骤中发生了什么, 即关键渲染路径。优化关键渲染路径是指优先显示与当前用户操做有关的内容。css
经过优化关键渲染路径,咱们能够显著缩短首次渲染页面的时间。 此外,了解关键渲染路径还能够为构建高性能交互式应用打下基础。html
渲染一个网页,浏览器须要完成的步骤:jquery
咱们的演示网页看起来可能很简单,实际上却须要完成至关多的工做。若是 DOM 或 CSSOM 被修改,您只能再执行一遍以上全部步骤,以肯定哪些像素须要在屏幕上进行从新渲染。web
优化关键渲染路径就是指最大限度缩短执行上述第 1 步至第 5 步耗费的总时间。 这样一来,就能尽快将内容渲染到屏幕上,此外还能缩短首次渲染后屏幕刷新的时间,即为交互式内容实现更高的刷新率。ajax
发现和解决关键渲染路径性能瓶颈须要充分了解常见的陷阱。 让咱们踏上实践之旅,找出常见的性能模式,从而帮助您优化网页。segmentfault
让咱们定义一下用来描述关键渲染路径的词汇:api
<html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Critical Path: No Style</title> </head> <body> <p>Hello <span>web performance</span> students!</p> <div><img src="awesome-photo.jpg"></div> </body> </html>
咱们将从基本 HTML 标记和单个图像(无 CSS 或 JavaScript)开始。让咱们在 Chrome DevTools 中打开 Network 时间线并检查生成的资源瀑布:浏览器
当 HTML 内容可用后,浏览器会解析字节,将它们转换成令牌,而后构建 DOM 树。请注意,为方便起见,DevTools 会在底部报告 DOMContentLoaded 事件的时间(216 毫秒),该时间一样与蓝色垂直线相符。HTML 下载结束与蓝色垂直线 (DOMContentLoaded) 之间的间隔是浏览器构建 DOM 树所花费的时间 — 在本例中仅为几毫秒。性能优化
请注意,咱们的“趣照”并未阻止 domContentLoaded 事件。这证实,咱们构建渲染树甚至绘制网页时无需等待页面上的每一个资产:并不是全部资源都对快速提供首次绘制具备关键做用。事实上,当咱们谈论关键渲染路径时,一般谈论的是 HTML 标记、CSS 和 JavaScript。图像不会阻止页面的首次渲染,不过,咱们固然也应该尽力确保系统尽快绘制图像!服务器
即使如此,系统仍是会阻止图像上的 load 事件(也称为 onload):DevTools 会在 335 毫秒时报告 onload 事件。回想一下,onload 事件标记的点是网页所需的全部资源均已下载并通过处理的点,这是加载微调框能够在浏览器中中止微调的点(由瀑布中的红色垂直线标记)。
T0 与 T1 之间的时间捕获的是网络和服务器处理时间。在最理想的状况下(若是 HTML 文件较小),咱们只需一次网络往返即可获取整个文档。因为 TCP 传输协议工做方式的缘故,较大文件可能须要更屡次的往返。所以,在最理想的状况下,上述网页具备单次往返(最少)关键渲染路径。
<html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <link href="style.css" rel="stylesheet"> </head> <body> <p>Hello <span>web performance</span> students!</p> <div><img src="awesome-photo.jpg"></div> </body> </html>
咱们一样须要一次网络往返来获取 HTML 文档,而后检索到的标记告诉咱们还须要 CSS 文件;这意味着,浏览器须要返回服务器并获取 CSS,而后才能在屏幕上渲染网页。所以,这个页面至少须要两次往返才能显示出来。CSS 文件一样可能须要屡次往返,所以重点在于最少。
这里有:
咱们同时须要 HTML 和 CSS 来构建渲染树。因此,HTML 和 CSS 都是关键资源:CSS 仅在浏览器获取 HTML 文档后才会获取,所以关键路径长度至少为两次往返。两项资源相加共计 9KB 的关键字节。
如今,让咱们向组合内额外添加一个 JavaScript 文件。
<html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <link href="style.css" rel="stylesheet"> </head> <body> <p>Hello <span>web performance</span> students!</p> <div><img src="awesome-photo.jpg"></div> <script src="app.js"></script> </body> </html>
咱们添加了 app.js,它既是网页上的外部 JavaScript 资源,又是一种解析器阻止(即关键)资源。更糟糕的是,为了执行 JavaScript 文件,咱们还须要进行阻止并等待 CSSOM;回想一下,JavaScript 能够查询 CSSOM,所以在下载 style.css 并构建 CSSOM 以前,浏览器将会暂停。
即使如此,若是咱们实际查看一下该网页的“网络瀑布”,就会注意到 CSS 和 JavaScript 请求差很少是同时发起的;浏览器获取 HTML,发现两项资源并发起两个请求。
所以,上述网页具备如下关键路径特性:
如今,咱们拥有了三项关键资源,关键字节总计达 11 KB,但咱们的关键路径长度还是两次往返,由于咱们能够同时传送 CSS 和 JavaScript。了解关键渲染路径的特性意味着可以肯定哪些是关键资源,此外还能了解浏览器如何安排资源的获取时间。让咱们继续探讨示例。
内联 JavaScript:
咱们减小了一个请求,但 onload 和 domContentLoaded 时间实际上没有变化。为何呢?怎么说呢,咱们知道,这与 JavaScript 是内联的仍是外部的并没有关系,由于只要浏览器遇到 script 标记,就会进行阻止,并等到 CSSOM 构建完毕。此外,在咱们的第一个示例中,浏览器是并行下载 CSS 和 JavaScript,而且差很少是同时完成。在此实例中,内联 JavaScript 代码并没有多大意义。可是,咱们能够经过多种策略加快网页的渲染速度。
向 script 标记添加“async”属性来解除对解析器的阻止:
<html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <link href="style.css" rel="stylesheet"> </head> <body> <p>Hello <span>web performance</span> students!</p> <div><img src="awesome-photo.jpg"></div> <script src="app.js" async></script> </body> </html>
异步(外部)JavaScript:
解析 HTML 以后不久即会触发 domContentLoaded 事件;浏览器已得知不要阻止 JavaScript,而且因为没有其余阻止解析器的脚本,CSSOM 构建也可同步进行了。
异步脚本具备如下几个优势:
所以,咱们优化过的网页如今恢复到了具备两项关键资源(HTML 和 CSS),最短关键路径长度为两次往返,总关键字节数为 9 KB。
若是 CSS 样式表只需用于打印,那会如何呢?
<html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <link href="style.css" rel="stylesheet" media="print"> </head> <body> <p>Hello <span>web performance</span> students!</p> <div><img src="awesome-photo.jpg"></div> <script src="app.js" async></script> </body> </html>
由于 style.css 资源只用于打印,浏览器没必要阻止它即可渲染网页。因此,只要 DOM 构建完毕,浏览器便具备了渲染网页所需的足够信息。所以,该网页只有一项关键资源(HTML 文档),而且最短关键渲染路径长度为一次往返。
为尽快完成首次渲染,咱们须要最大限度减少如下三种可变因素:
关键资源是可能阻止网页首次渲染的资源。这些资源越少,浏览器的工做量就越小,对 CPU 以及其余资源的占用也就越少。
一样,关键路径长度受全部关键资源与其字节大小之间依赖关系图的影响:某些资源只能在上一资源处理完毕以后才能开始下载,而且资源越大,下载所需的往返次数就越多。
最后,浏览器须要下载的关键字节越少,处理内容并让其出如今屏幕上的速度就越快。要减小字节数,咱们能够减小资源数(将它们删除或设为非关键资源),此外还要压缩和优化各项资源,确保最大限度减少传送大小。
优化关键渲染路径的常规步骤以下:
做为每一个可靠性能策略的基础,准确的评估和检测必不可少。 没法评估就谈不上优化。本文说明了评估 CRP(关键渲染路径)
性能的不一样方法。
由于 style.css 资源只用于打印,浏览器没必要阻止它即可渲染网页。因此,只要 DOM 构建完毕,浏览器便具备了渲染网页所需的足够信息。所以,该网页只有一项关键资源(HTML 文档),而且最短关键渲染路径长度为一次往返。
关键请求链这个概念源自关键渲染路径 (CRP) 优化策略。 CRP 经过肯定优先加载的资源以及加载顺序,容许浏览器尽量快地加载页面。
在 Lighthouse 的 Chrome 扩展程序版本中,您的报告将生成一个相似以下的图表:
Initial navigation |---lighthouse/ (developers.google.com) |---/css (fonts.googleapis.com) - 1058.34ms, 72.80KB |---css/devsite-googler-buttons.css (developers.google.com) - 1147.25ms, 70.77KB |---jsi18n/ (developers.google.com) - 1155.12ms, 71.20KB |---css/devsite-google-blue.css (developers.google.com) - 2034.57ms, 85.83KB |---2.2.0/jquery.min.js (ajax.googleapis.com) - 2699.55ms, 99.92KB |---contributors/kaycebasques.jpg (developers.google.com) - 2841.54ms, 84.74KB |---MC30SXJEli4/photo.jpg (lh3.googleusercontent.com) - 3200.39ms, 73.59KB
此图表表示页面的关键请求链。从 lighthouse/ 到 /css 的路径造成一条链。 从 lighthouse/ 到 css/devsite-googler-buttons.css
的路径造成另外一条链。 以此类推。审查的最高得分体现了这些链条的数量。 例如,上面的图表的分数为七分。
该图表也详细列出下载每一个资源花了多少时间,以及下载每一个资源所需的字节数。
您能够根据此图表利用如下方式提高您的 CRP:
优化以上任一因素均可提高页面加载速度。
详情请参阅关键请求链
结合使用 Navigation Timing API 和页面加载时发出的其余浏览器事件,您能够捕获并记录任何页面的真实 CRP 性能。
上图中的每个标签都对应着浏览器为其加载的每一个网页追踪的细粒度时间戳。
那么,这些时间戳有什么含义呢?
domContentLoaded:表示 DOM 准备就绪而且没有样式表阻止 JavaScript 执行的时间点,这意味着如今咱们能够构建渲染树了。
HTML 规范中规定了每一个事件的具体条件:应在什么时候触发、应知足什么条件等等。对咱们而言,咱们将重点放在与关键渲染路径有关的几个关键里程碑上:
若是没有阻塞解析器的 JavaScript,则 DOMContentLoaded 将在 domInteractive 后当即触发。
要以最快速度完成首次渲染,须要最大限度减小网页上关键资源的数量并(尽量)消除这些资源,最大限度减小下载的关键字节数,以及优化关键路径长度。
为了最大限度减小浏览器渲染网页的工做量,应延迟任何非必需的脚本(即对构建首次渲染的可见内容可有可无的脚本)。
CSS 是构建渲染树的必备元素,首次构建网页时,JavaScript 经常受阻于 CSS。确保将任何非必需的 CSS 都标记为非关键资源(例如打印和其余媒体查询),并应确保尽量减小关键 CSS 的数量,以及尽量缩短传送时间。
尽早在 HTML 文档内指定全部 CSS 资源,以便浏览器尽早发现 <link> 标记并尽早发出 CSS 请求。
一个样式表可使用 CSS import (@import) 指令从另外一样式表文件导入规则。不过,应避免使用这些指令,由于它们会在关键路径中增长往返次数:只有在收到并解析完带有 @import 规则的 CSS 样式表以后,才会发现导入的 CSS 资源。