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

当您打开网页时,浏览器会向服务器请求 HTML 文档,解析其内容,并针对任何引用的资源提交单独的请求。作为开发者,您已经知道网页需要的所有资源,以及哪些资源最重要。您可以利用这些知识提前请求关键资源,从而加快加载流程。本文介绍了如何使用 <link rel="preload"> 实现此目的。

预加载的工作原理

预加载最适合通常由浏览器较晚发现的资源。

Chrome DevTools 网络面板的屏幕截图。
在此示例中,Pacifico 字体是在样式表中使用 @font-face 规则定义的。浏览器只有在下载并解析样式表后,才会加载字体文件。

通过预加载某个资源,您是在告知浏览器您希望在浏览器发现该资源之前提前提取该资源,因为您确定该资源对当前网页很重要。

应用预加载后 Chrome DevTools 网络面板的屏幕截图。
在此示例中,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 的最简单方法是向文档的 <head> 添加 <link> 标记:

<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 的影响

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

Largest Contentful Paint (LCP)

对于字体和图片,预加载对 Largest Contentful Paint (LCP) 有很大影响,因为图片和文本节点都可能是 LCP 候选项。使用 Web 字体呈现的主打图片和大量文本可以通过恰当放置的预加载提示显著受益,因此,当有机会更快地向用户传送这些重要内容时,应使用预加载提示。

不过,在预加载和其他优化方面,您需要格外小心!尤其要避免预加载过多资源。如果优先级资源过多,实际上就没有任何资源具有优先级。过多预加载提示的影响对网络速度较慢的设备尤为不利,因为带宽争用会更加明显。

而是应着重考虑一些您知道通过恰当的预加载会受益的高价值资源。预加载字体时,请确保以 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 属性来管理字体加载方式的网页字体之间存在显著的相互作用。如需尽可能减少与 Web 字体相关的布局偏移,请考虑以下策略:

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

在 Web 字体方面,CLS 是一项难以优化的指标。一如既往,请在实验室中进行实验,但要依靠现场数据来确定您的字体加载策略是改善了 CLS 还是加剧了 CLS。

Interaction to Next Paint (INP)

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

此外,您还需要注意如何进行代码分块。代码拆分是一项非常有效的优化,可减少启动期间加载的 JavaScript 量,但如果互动依赖于在互动开始时加载的 JavaScript,则可能会出现延迟。为了弥补这一点,您需要检查用户的意图,并在互动发生之前为必要的 JavaScript 分块注入预加载。例如,当表单中的任何字段获得焦点时,预加载验证表单内容所需的 JavaScript。

总结

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