预加载关键资源,以提高加载速度

当您打开网页时,浏览器会向服务器请求 HTML 文档,解析其内容,然后针对引用的任何资源提交单独的请求。作为开发者,您已经了解自己的网页需要的所有资源以及其中哪些资源最重要。您可以根据这些信息提前请求关键资源,并加快加载过程。这篇博文介绍了如何使用 <link rel="preload"> 实现该目标。

预加载的运作方式

预加载最适合浏览器通常在较晚时间才发现的资源。

Chrome DevTools Network 面板的屏幕截图。
在此示例中,系统会使用 @font-face 规则在样式表中定义 Pacifico 字体。浏览器仅在下载和解析完样式表之后才加载字体文件。

预加载某个资源,即告知浏览器您希望比浏览器发现该资源更早地提取该资源,因为您确定它对当前网页很重要。

应用预加载后的 Chrome DevTools Network 面板的屏幕截图。
在此示例中,预加载了 Pacifico 字体,因此下载与样式表并行进行。

关键请求链表示浏览器优先获取和提取的资源的顺序。Lighthouse 将位于此链的第三级别的资产识别为延迟发现的资产。您可以使用预加载密钥请求审核来确定要预加载的资源。

Lighthouse 的预加载密钥请求审核。

您可以向 HTML 文档的标头添加包含 rel="preload"<link> 标记,从而预加载资源:

<link rel="preload" as="script" href="critical.js">

浏览器会缓存预加载的资源,以便在需要时立即可用。(它不会执行脚本或应用样式表。)

系统会按照浏览器认为合适的方式执行资源提示,例如 preconnectprefetch。另一方面,preload 对于浏览器来说是强制性的。现代浏览器已经非常擅长优先处理资源,正因如此,务必谨慎使用 preload,并且仅预加载最关键的资源。

未使用的预加载项会在 load 事件发生大约 3 秒后在 Chrome 中触发控制台警告。

Chrome 开发者工具控制台关于未使用的预加载资源的警告。

用例

预加载 CSS 中定义的资源

只有在浏览器下载并解析这些 CSS 文件后,系统才会发现使用 @font-face 规则定义的字体或 CSS 文件中定义的背景图片。预加载这些资源可确保在 CSS 文件下载之前提取它们。

预加载 CSS 文件

如果您使用的是关键 CSS 方法,则可以将 CSS 拆分为两部分。呈现首屏内容所需的关键 CSS 被内嵌在文档的 <head> 中,非关键 CSS 通常使用 JavaScript 进行延迟加载。在加载非关键 CSS 之前等待 JavaScript 执行可能会导致在用户滚动时呈现延迟,因此最好使用 <link rel="preload"> 尽早启动下载。

预加载 JavaScript 文件

由于浏览器不会执行预加载的文件,因此预加载有助于将提取操作与执行操作分开,从而提高可交互时间等指标。如果您拆分 JavaScript 软件包并仅预加载关键区块,则预加载的效果最佳。

如何实现 rel=preload

实现 preload 最简单的方法是将 <link> 标记添加到文档的 <head> 中:

<head>
  <link rel="preload" as="script" href="critical.js">
</head>

提供 as 属性有助于浏览器根据预提取资源的类型设置其优先级、设置正确的标头,以及确定缓存中是否已存在该资源。此属性接受的值包括:scriptstylefontimage其他

某些类型的资源(例如字体)以匿名模式加载。对于这些库,您必须使用 preload 设置 crossorigin 属性:

<link rel="preload" href="ComicSans.woff2" as="font" type="font/woff2" crossorigin>

<link> 元素还接受 type 属性,该属性包含关联资源的 MIME 类型。浏览器会使用 type 属性的值来确保仅当相应资源的文件类型受支持时才会预加载资源。如果浏览器不支持指定的资源类型,将忽略 <link rel="preload">

您还可以通过 Link HTTP 标头预加载任何类型的资源:

Link: </css/style.css>; rel="preload"; as="style"

在 HTTP 标头中指定 preload 的好处是,浏览器无需解析文档即可发现它,这在某些情况下可以提供细微的改进。

使用 webpack 预加载 JavaScript 模块

如果您使用模块打包器来创建应用的 build 文件,则需要检查其是否支持注入预加载标记。在 webpack 4.6.0 或更高版本中,可通过在 import() 中使用魔法注释来支持预加载:

import(_/* webpackPreload: true */_ "CriticalChunk")

如果您使用的是旧版 webpack,请使用第三方插件,例如 preload-webpack-plugin

预加载对 Core Web Vitals 的影响

预加载是一项强大的性能优化,对加载速度有影响。此类优化可能会导致您网站的核心网页指标发生变化,请务必注意这一点。

Largest Contentful Paint (LCP)

在字体和图片方面,预加载对 Largest Contentful Paint (LCP) 有巨大影响,因为图片和文本节点都可能是 LCP 候选。使用网页字体呈现的主打图片和大段文字可以从恰当的预加载提示中显著受益,并应在有机会更快地向用户提供这些重要信息时使用。

不过,在预加载和其他优化方面,您需要非常小心!尤其要避免预加载过多资源。如果太多资源的优先级过高,实际上并不会造成任何优先级。预加载提示过多的影响对于带宽争用更为明显的较慢网络的用户来说尤其不利。

而是应该将重点放在少数高价值资源上,这样做的好处是,部署妥当的预加载功能会让这些资源受益。预加载字体时,请确保您提供 WOFF 2.0 格式的字体,以尽可能缩短资源加载时间。由于 WOFF 2.0 具有卓越的浏览器支持,因此如果 LCP 候选版本是文本节点,使用 WOFF 1.0 或 TrueType (TTF) 等旧格式会导致 LCP 延迟。

对于 LCP 和 JavaScript,您需要确保从服务器发送完整的标记,才能使浏览器的预加载扫描程序正常工作。如果您提供的体验完全依赖于 JavaScript 来呈现标记,并且无法发送由服务器呈现的 HTML,那么,在浏览器预加载扫描器无法执行这项操作时,预加载只有在 JavaScript 完成加载和执行后才可发现的资源,将会大有益处。

Cumulative Layout Shift (CLS)

累计布局偏移 (CLS) 在涉及网页字体方面是一项特别重要的指标,而 CLS 与使用 font-display CSS 属性管理字体加载方式的网页字体之间存在显著相互影响。为了最大限度地减少与网页字体相关的布局偏移,请考虑采取以下策略:

  1. 在为 font-display 使用默认 block 值时,预加载字体。这是一种微妙的平衡。在没有回退的情况下阻止显示字体可能会被视为用户体验问题。一方面,使用 font-display: block; 加载字体可消除与网络字体相关的布局偏移。另一方面,如果这些网页字体对用户体验至关重要,您仍希望尽快加载它们。将预加载与 font-display: block; 相结合或许是可以接受的折衷方案。
  2. font-display 使用 fallback 值时,预加载字体。fallbackswapblock 之间的折衷方案,其阻塞期极短。
  3. 为没有预加载功能的 font-display 使用 optional 值。如果网页字体对用户体验不重要,但仍用于呈现大量网页文本,请考虑使用 optional 值。在不利条件下,optional 会以后备字体显示网页文字,同时在后台加载字体以供下次导航使用。这些条件下的最终结果会改善 CLS,因为系统字体会立即呈现,而后续网页加载时则会立即加载字体,而不会改变布局。

就网页字体而言,CLS 是一项难以优化的指标。与往常一样,请在实验室中进行实验,但请相信您的实测数据,以确定您的字体加载策略是提高了 CLS 还是使情况变差。

Interaction to Next Paint (INP)

Interaction to Next Paint 是一个指标,用于衡量对用户输入的响应。由于网络上互动性最大的份额是由 JavaScript 驱动的,因此预加载可为重要互动提供支持的 JavaScript 可能有助于降低网页的 INP。但请注意,如果在启动期间预加载过多的 JavaScript,如果争用带宽的资源过多,可能会产生意想不到的负面影响。

此外,您还需要谨慎处理代码拆分。代码拆分是减少启动期间加载的 JavaScript 量的绝佳优化,但如果互动依赖于在互动开始时加载的 JavaScript,则可能会延迟。为弥补这一点,您需要检查用户的 intent,并在交互发生之前为必要的 JavaScript 块注入预加载。例如,预加载表单中的任何字段都获得焦点时验证表单内容所需的 JavaScript。

总结

为了提高网页速度,请预加载浏览器延迟发现的重要资源。预加载所有内容都会适得其反,因此请谨慎使用 preload,并衡量实际影响