在上一节课程中,我们学习了 CSS 和 JavaScript 等页面资源如何影响页面加载速度,并探讨了如何优化这些资源及其传输,以加快页面渲染。现在是深入了解资源加载进阶话题的绝佳时机,这包括使用资源提示(resource hint)帮助浏览器更迅速地完成加载工作。
资源提示能够通过预告浏览器如何加载以及优先处理各种资源,进而帮助开发人员进一步缩短页面加载时间。最初引入的资源提示包括 preconnect
和 dns-prefetch
。后来,preload
和 Fetch Priority API 相继问世,再次增强了浏览器预加载能力。
资源提示可以让浏览器提前执行某些操作以提升加载效率,比如进行早期的 DNS 查询、提前与服务器建立连接,甚至在浏览器正常流程发现资源之前就开始抓取资源。
资源提示通常会在 HTML 文件的 <head>
设置,或者作为 HTTP 头信息传递。本文将涵盖 preconnect
、dns-prefetch
和 preload
,以及 prefetch
提供的预加载行为。
preconnect
preconnect
用于提前建立关键资源的源之间的连接。比如图片或其他文件可能存放在内容分发网络(CDN)或其他不同的源上:
通过使用 preconnect
,你可以预先指导浏览器准备与某个特定的跨域服务器建立连接,而且建议浏览器尽快展开连接,最好是在 HTML 解析器或预加载扫描器介入之前。
如果你的网页上包含了大量跨域资源,你应该为页面的关键资源优先使用 preconnect
。
一个常见的 preconnect
使用案例是 Google 字体。谷歌字体推荐用户预先连接到 https://fonts.googleapis.com 网址,该网址负责提供 @font-face
的字体定义;连接到 https://fonts.gstatic.com 网址,用于提供字体文件。
所谓 crossorigin
属性,它是用来标示是否需要通过跨源资源共享(CORS)协议来请求资源。当你利用 preconnect
技术时,如果你需要下载的资源是来自支持 CORS 的网站,例如字体文件,你就得在使用 preconnect
提示时加上 crossorigin
属性。
CAUTION
如果你没有添加 crossorigin
属性,浏览器在下载字体文件时将会重新建立一个新的连接,这样就无法复用之前用 preconnect
提示所建立的连接了。
dns-prefetch
虽然预先与跨域服务器建立连接能显著提升页面首次加载速度,但一次性建立多个跨源连接可能既不现实也不务实。如果你担心可能过度使用了 preconnect
,那么一种成本更低的资源提示替代方案是使用 dns-prefetch
。
顾名思义,dns-prefetch
本身并不建立和跨域服务器的链接,它仅仅是提前完成域名的 DNS 查询。DNS 查询是指将域名解析为其对应的 IP 地址的过程。尽管在设备和网络级别有 DNS 缓存层可以加速此过程,但完成 DNS 查询仍旧需要消耗一定的时间。
DNS 查询的成本相对较低,某些情况下它可能比 preconnect
更适合我们的需求。特别是对于那些你认为用户很可能点击的通向其他网站的链接,DNS 查询可能是更理想的资源提示方式。dnstradamus 就是这样一款工具,它能够自动通过 JavaScript 来实现这个功能,并利用 Intersection Observer API 在用户滑动查看页面并将其他网站的链接滚动到视窗中时,自动将 dns-prefetch
提示注入到当前页面的 HTML 中。
preload
preload
用于提前请求渲染页面时必须的资源:
预加载指令应专门用于那些较晚被发现,但对页面渲染至关重要的资源。它通常用于预加载字体文件、通过 @import
语句加载的 CSS 文件,或者那些很可能成为页面 LCP 的 CSS background-image
。这些情况下的文件通常无法被预加载扫描器识别,因为这些资源是在其他的外部资源文件中被引用的。
CAUTION
如果使用 preload
来下载 <img>
元素指明的、会根据用户视口尺寸变化的图片,请务必在 preload
中添加 imagesrcset
属性,以确保为当前视窗下载正确的图片版本。同时,应该省略 src
属性,以防不支持响应式预加载的浏览器下载 fallback 图片。
和 preconnect
类似,preload
在预加载跨源资源(例如字体)时,需要添加 crossorigin
属性。如果忘记添加 crossorigin
属性,或者错误地为非跨源请求添加了此属性,浏览器会重复下载资源两次,这会无谓地消耗原本可以用于加载其他资源的带宽。
IMPORTANT
如果 <link>
元素的预加载指令中没有包含 as
属性,指定的资源会被下载两次。你可以访问 MDN 文档,查询 as
属性值的详细列表。
在前面的 HTML 代码片段中,尽管 /font.woff2
位于同域,浏览器仍被指示通过 CORS 请求预先加载这个资源。
CAUTION
preload
是一个极具影响力的性能优化手段——实际上,它有时会被过度使用。通过 preload
下载的资源被赋予了高优先级,在过度使用时,预加载可能会引发带宽竞争,这会对页面的加载速度产生负面影响。
prefetch
IMPORTANT
这里只是对 prefetch
进行了一个基本介绍,并没有深入讨论当你在使用它时应该考虑的一些细节和折中选择。本课程的后续部分将会详细说明如何低优先级预获取不久的将来可能需要的资源,以及如何预获取网页以便加速未来的页面跳转。
prefetch
的作用是发起一个低优先级的请求,用于获取将来浏览时可能会用到的资源:
这个指令的格式与 preload
非常相似,区别在于 <link>
元素的 rel 属性此时应设为 prefetch
。但不同于 preload
的是,prefetch
是偏向于猜测性质的,你获取的是对未来可能用到、也可能不用到的资源。
prefetch
在某些场景下是非常有帮助的——比如,如果你发现网站上有一个大多数用户都会遵循的流程,那么对那些未来页面的渲染关键资源进行 prefetch
能帮助减少它们的加载时间。
CAUTION
由于 prefetch
的操作基于预测,存在一个潜在的缺点,那就是如果用户最终没有访问到需要预先获取资源的页面,那么用于获取这些资源的数据可能就会被浪费。你需要依据自身网站的用户行为分析或其他数据来源来判定是否使用 prefetch
。另一种方式是你可以利用 Save-Data 提示,对那些想要节省流量的用户禁用 prefetch
功能。
Fetch Priority API
你可以利用 fetchpriority
属性应用 Fetch Priority API,以此来增加某个资源的加载优先级。这个属性可以与 <link>
、<img>
和 <script>
这些元素一同使用。
IMPORTANT
fetchpriority
属性在用于优化页面的最大内容绘制(LCP)图像时特别有用。通过提升 LCP 图像的加载优先级,你可以比较简单地提高页面的 LCP 性能。
在默认情况下,图片资源是以低优先级来加载的。当页面布局完成后,如果图片出现在初始视口范围内,它的下载优先级则会提升到高优先级。在上文提到的 HTML 代码片段中,使用 fetchpriority
属性可以立即指示浏览器优先下载重要的 LCP 大图,而其他不那么关键的缩略图则维持较低的下载优先级。
现代浏览器通常会分两个阶段来加载资源。第一阶段专为关键资源预留,这个阶段会在所有的阻塞脚本下载并执行完毕后结束。在这个阶段,那些被标记为 low 优先级的资源可能会被推迟下载。通过设置 fetchpriority="high"
,你可以提高资源的加载优先级,确保浏览器能在第一阶段就下载该资源。
资源提示 demo
注意:demo 托管在 glitch,可能需要科学上网
https://learn-performance-resource-hints.glitch.me/