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

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

预加载的工作原理

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

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

通过预加载特定资源,即可告知浏览器您希望比浏览器更早地提取它,因为您确定它对当前网页很重要。

应用预加载后的 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 的最简单方法是向文档的 <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 候选项。以网页字体呈现的主打图片和大段文字可显著受益于精心放置的预加载提示,因此在有机会更快地向用户提供这些重要内容时,应使用此提示。

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

相反,请重点关注您知道会受益于恰当预加载的一些高价值资源。预加载字体时,请确保以 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 字体相关的布局偏移。另一方面,如果这些网页字体对于用户体验至关重要,您仍然希望尽快加载它们。将预加载与 font-display: block; 结合使用或许是一种可接受的折衷做法。
  2. 在使用 font-displayfallback 值时预加载字体。fallbackswapblock 之间的折衷方案,因为它的屏蔽期非常短。
  3. font-display 使用 optional 值,不进行预加载。如果某种网页字体对用户体验而言并不是很重要,但仍用于呈现大量网页文本,请考虑使用 optional 值。在恶劣条件下,optional 会使用后备字体显示网页文本,同时在后台加载字体以供下次导航。在这些情况下,最终结果是 CLS 会得到改善,因为系统字体会立即呈现,而后续的网页加载会立即加载字体,而不会出现布局偏移。

当涉及网络字体时,CLS 是一个很难优化的指标。与往常一样,在实验室中进行实验,但请相信您的实测数据,以确定您的字体加载策略是提升还是降低 CLS。

Interaction to Next Paint (INP)

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

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

总结

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