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

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

预加载的运作方式

预加载最适合浏览器通常会延迟发现的资源。

Chrome 开发者工具“网络”面板的屏幕截图。
在此示例中,Pacifico 字体是在样式表中通过 @font-face 规则定义的。浏览器仅在完成样式表的下载和解析后才加载字体文件。

通过预加载特定资源,您可以告知浏览器您希望比浏览器发现该资源的时间更早地获取该资源,因为您确信该资源对当前网页很重要。

应用预加载后的 Chrome 开发者工具“网络”面板的屏幕截图。
在此示例中,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 延迟加载。在执行 JavaScript 之前等待加载非关键 CSS 可能会导致用户滚动时渲染延迟,因此最好使用 <link rel="preload"> 更快地启动下载。

预加载 JavaScript 文件

由于浏览器不会执行预加载的文件,因此预加载有助于将提取与执行分开,从而改进“可交互时间”等指标。如果您拆分 JavaScript 代码包并仅预加载关键 chunk,则预加载效果最佳。

如何实现 rel=preload

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

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

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

某些类型的资源(例如字体)会在匿名模式下加载。对于这些 intent,您必须将 crossorigin 属性设置为 preload

<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

预加载对核心网页指标的影响

预加载是一项强大的性能优化功能,可影响加载速度。此类优化可能会导致网站的核心 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; 加载字体可消除与网页字体相关的布局偏移。另一方面,如果这些网络字体对用户体验至关重要,您仍然希望尽快加载它们。将预加载与 font-display: block; 相结合可能是一种可接受的折衷方案。
  2. 在为 font-display 使用 fallback 值时预加载字体。fallbackswapblock 之间的折衷方案,因为它的屏蔽期非常短。
  3. 使用不含预加载的 font-displayoptional 值。如果某种 Web 字体对用户体验并非至关重要,但仍用于呈现大量网页文字,请考虑使用 optional 值。在不利条件下,optional 会以备用字体显示网页文字,同时在后台加载字体以供下次导航使用。在这些条件下,最终结果是 CLS 得到改善,因为系统字体会立即呈现,而后续网页加载会立即加载字体,而不会发生布局偏移。

就 Web 字体而言,CLS 是一项难以优化的指标。与往常一样,您可以在实验室中进行实验,但要相信实地数据,以便确定字体加载策略是改善了 CLS 还是使其变得更糟。

Interaction to Next Paint (INP)

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

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

总结

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