预加载资源以加速未来导航

了解 rel=prefetch 资源提示及其使用方法。

发布时间:2019 年 9 月 12 日,上次更新时间:2025 年 2 月 8 日

研究表明,更快的加载时间可以提高转化率,改善用户体验。如果您了解用户如何在您的网站中移动以及他们接下来可能访问哪些页面,则可以通过提前下载这些页面的资源来缩短未来导航的加载时间。

本指南介绍了如何使用 <link rel=prefetch> 来实现这一目标,从而有效地实现预加载。

使用 rel=prefetch 提升网站性能

在网页中添加 <link rel=prefetch> 会告知浏览器下载用户将来可能需要的一些资源(如脚本或 CSS 文件)。

<link rel="prefetch" href="/css/styles.css">

prefetch 提示会消耗额外的字节来加载非立即需要的资源,因此需要慎重应用此技术;仅当您确信用户需要这些资源时才预加载它们。当用户连接速度较慢时,请考虑不要预加载。您可以使用 Network Information API 检测到这种情况。

用例

预加载有很多用例,可以通过提前下载资源来加速网页。

预加载后续页面

当后续页面可预测时,预加载 HTML 文档,以便在单击链接时立即加载页面。

例如,在产品列表页面中,您可以预加载列表中最受欢迎产品的页面。在某些情况下,下一个导航甚至更容易预测 - 在购物车页面上,用户访问结帐页面的可能性通常很高,这使其成为预加载的良好候选对象。

虽然预加载资源确实会使用额外的带宽,但它可以改善大多数性能指标。首字节时间 (TTFB) 通常会低得多,因为文档请求会导致缓存命中。由于 TTFB 会更低,因此后续基于时间的指标通常也会更低,包括 最大内容绘制 (LCP)首次内容绘制 (FCP)

预加载静态资源

当可以预测用户可能访问的后续部分时,预加载静态资源,例如脚本或样式表。当这些资源在多个页面之间共享时,这尤其有用。

例如,Netflix 利用用户在注销页面上花费的时间来预加载 React,用户登录后将使用 React。 благодаря этому они 将未来导航的 Time to Interactive 缩短了 30%

预加载静态资源对性能指标的影响取决于预加载的资源

  • 预加载图像可以显着缩短 LCP 图像元素的 LCP 时间。
  • 预加载样式表可以改善 FCP 和 LCP,因为将消除下载样式表的网络时间。由于样式表是渲染阻塞的,因此在预加载时可以减少 LCP。在后续页面的 LCP 元素是使用 background-image 属性请求的 CSS 背景图像的情况下,该图像也将作为预加载样式表的从属资源进行预加载。
  • 预加载 JavaScript 将使预加载脚本的处理比在导航期间首先需要通过网络获取时发生得更快。这可能会对页面的 Interaction to Next Paint (INP) 产生影响。在使用 JavaScript 在客户端渲染标记的情况下,可以通过减少资源加载延迟来改善 LCP,并且可以更快地进行包含页面 LCP 元素的标记的客户端渲染。
  • 预加载当前页面尚未使用 Web 字体可以消除布局偏移。在使用 font-display: swap; 的情况下,字体交换期被消除,从而加快了文本的渲染并消除了布局偏移。如果未来页面使用预加载的字体,并且页面的 LCP 元素是使用 Web 字体的文本块,则该元素的 LCP 也将更快。

预加载按需 JavaScript 代码块

代码拆分 JavaScript 包使您可以最初仅加载应用程序的一部分,并延迟加载其余部分。如果您正在使用此技术,则可以将预加载应用于当前不需要但可能很快会被请求的路由或组件。

例如,如果您的页面包含一个按钮,该按钮打开一个包含表情符号选择器的对话框,则可以将其分为三个 JavaScript 代码块 - home、dialog 和 picker。可以最初加载 home 和 dialog,而可以按需加载 picker。诸如 webpack 之类的工具允许您指示浏览器预加载这些按需代码块。

如何实现 rel=prefetch

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

<head>
  ...
  <link rel="prefetch" href="//css/styles.css">
  ...
</head>

您还可以使用 Link HTTP 标头 启动预加载。

Link: </css/style.css>; rel=prefetch

在 HTTP 标头中指定预加载提示的好处是,浏览器不需要解析文档来查找资源提示,这在某些情况下可以提供小的改进。

使用 webpack magic comments 预加载 JavaScript 模块

webpack 允许您预加载脚本,用于您相当确定用户将很快访问或使用的路由或函数。

以下代码片段从 lodash 库延迟加载排序函数,以对将由表单提交的一组数字进行排序。

form.addEventListener("submit", e => {
  e.preventDefault()
  import('lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

与其等待 'submit' 事件发生来加载此函数,不如预加载此资源,以增加在用户提交表单时将其缓存在缓存中可用的机会。 webpack 允许使用 import() 内的 magic comments 来实现这一点。

form.addEventListener("submit", e => {
   e.preventDefault()
   import(/* webpackPrefetch: true */ 'lodash.sortby')
         .then(module => module.default)
         .then(sortInput())
         .catch(err => { alert(err) });
});

这会告知 webpack 将 <link rel="prefetch"> 标记注入到 HTML 文档中。

<link rel="prefetch" href="1.bundle.js">

预加载按需代码块的性能优势有点细微,但总的来说,您可以期望看到对依赖于这些按需代码块的交互的更快响应,因为它们将立即可用。根据交互的性质,这可能会对页面的 INP 带来好处。

总的来说,预加载也影响到整体资源优先级。当预加载资源时,它以尽可能最低的优先级完成。因此,任何预加载的资源都不会与当前页面中所需的资源争夺带宽。

您还可以使用在底层使用 prefetch 的库来实现更智能的预加载。

quicklink 和 Guess.js 都使用 Network Information API 来避免在用户网络速度较慢或已开启 Save-Data 的情况下进行预加载。

底层预加载

资源提示不是强制性指令,是否以及何时执行它们取决于浏览器。

您可以在同一页面中多次使用预加载。浏览器将所有提示排队,并在空闲时请求每个资源。在 Chrome 中,如果预加载尚未完成加载,并且用户导航到预定的预加载资源,则浏览器会将正在进行的加载作为导航拾取(其他浏览器供应商可能会以不同的方式实现此目的)。

预加载以“最低”优先级进行,因此预加载的资源不会与当前页面中所需的资源争夺带宽。

如果资源可缓存,则预加载的文件将存储在 HTTP 缓存中,否则将被丢弃且不使用。

结论

使用 prefetch 可以通过提前下载未来需要的资源来大大提高网站的 Web 性能。prefetch 在大多数现代浏览器中都受支持。此技术需要加载可能不使用的额外字节,因此在使用时请注意;仅在必要时执行此操作,理想情况下,仅在快速网络上执行。