预加载

**预加载**指的是在实际访问相关页面之前执行导航操作(例如 DNS 获取、获取资源或渲染文档)的做法,这些操作基于对用户最有可能访问的下一个页面的预测。

这些预测可以由开发者提供(例如,他们网站上最受欢迎的目的地列表),也可以由浏览器启发式算法确定(例如,基于用户历史记录中的热门网站)。当成功使用时,此类技术可以通过更快地提供页面或在某些情况下立即提供页面来显著提高性能。

此页面回顾了可用的预加载技术以及何时可以以及应该使用它们来提高性能。

预加载机制

预加载有几种机制

  • **预取**涉及在需要之前获取渲染文档(或文档的一部分)所需的一些或全部资源,以便在需要渲染时,可以更快地完成渲染。
  • **预渲染**更进一步,实际上渲染了内容,以便在需要时显示。根据执行方式,这可能导致从旧页面到新页面的即时导航。
  • **预连接**通过抢先执行部分或全部连接握手(即 DNS + TCP + TLS)来加快未来从给定来源加载的速度。

**注意:**以上描述是高级和通用的。浏览器为实现预取和预渲染而执行的操作的确切内容取决于使用的功能。更精确的功能描述在下面的预加载功能部分提供。

预加载是如何实现的?

预加载主要通过两种方式实现。

首先,一些浏览器会根据各种启发式算法自动预渲染页面,以提供自动性能改进。执行方式的确切内容取决于浏览器实现。例如,Chrome 在地址栏中键入匹配字符串时会自动预渲染页面——如果它非常确定您将访问该页面(请参阅查看 Chrome 的地址栏预测以了解更多详细信息)。此外,当在地址栏中键入搜索词时,它可能会自动预渲染搜索结果页面,前提是搜索引擎已指示这样做。它使用与Speculation Rules API相同的机制执行此操作。

其次,开发人员可以使用多种不同的平台功能来提供有关他们希望浏览器执行的预加载操作的说明。下一节将对此进行回顾。

预加载功能

<link rel="preconnect"> 向浏览器提供提示,表明用户可能需要来自指定资源来源的资源,因此浏览器可以通过抢先启动到该来源的连接来提高性能。支持的浏览器将抢先执行部分或全部连接握手(即 DNS + TCP + TLS)。

例如

html
<link rel="preconnect" href="https://example.com" />

<link rel="preconnect"> 在浏览器中得到广泛支持,并将为任何未来的跨源 HTTP 请求、导航或子资源带来好处。它对同源请求没有好处,因为连接已打开。

如果页面需要与许多第三方域建立连接,则预连接所有域可能适得其反。<link rel="preconnect"> 提示最好仅用于最关键的连接。对于其他连接,只需使用<link rel="dns-prefetch"> 来节省第一步(DNS 查找)的时间。

您还可以将预连接作为 HTTP Link 标头实现,例如

http
Link: <https://example.com>; rel="preconnect"

<link rel="dns-prefetch"> 向浏览器提供提示,表明用户可能需要来自指定资源来源的资源,因此浏览器可以通过抢先执行该来源的 DNS 解析来提高性能。它与<link rel="preconnect">相同,只是它只处理 DNS 部分。

同样,浏览器支持广泛,并且它对同源请求没有好处,因为连接已打开。

例如

html
<link rel="dns-prefetch" href="https://example.com" />

**注意:**有关更多详细信息,请参阅使用 dns-prefetch

<link rel="preload"> 向浏览器提供提示,说明当前页面上的哪些资源是高优先级的,因此当它在页面的<head>中看到<link> 元素时,它可以尽早开始下载这些资源。

例如

html
<link rel="preload" href="main.js" as="script" />
<!-- CORS-enabled preload -->
<link
  rel="preload"
  href="https://www.example.com/fonts/cicle_fina-webfont.woff2"
  as="font"
  type="font/woff2"
  crossorigin />

结果保存在每个文档的内存缓存中。如果您预加载当前页面未使用为子资源的内容,则通常会浪费资源,尽管如果标头允许,结果可能会填充 HTTP 缓存。

您还可以将预加载作为 HTTP Link 标头实现,例如

http
Link: <https://www.example.com/fonts/cicle_fina-webfont.woff2>; rel="preload"

现代浏览器中对<link rel="preload">/<link rel="modulepreload">的支持非常广泛。

<link rel="modulepreload"> 向浏览器提供提示,说明当前页面上的哪些 JavaScript 模块是高优先级的,因此它可以尽早开始下载这些模块。

例如

js
<link rel="modulepreload" href="main.js" />

它是JavaScript 模块<link rel="preload">的专门版本,并且工作方式基本相同。但是,也有一些区别

  • 浏览器知道该资源是 JavaScript 模块,因为不需要as属性,并且它可以使用正确的凭据模式来避免重复获取。
  • 浏览器不仅下载它并将其存储在缓存中,还会下载它,然后将其直接解析并编译到内存模块映射中。
  • 浏览器还可以自动对模块依赖项执行相同的操作。

<link rel="prefetch"> 向浏览器提供提示,表明用户可能需要目标资源用于未来的导航,因此浏览器可以通过抢先获取和缓存资源来提高用户体验。<link rel="prefetch"> 用于同站点导航资源,或同站点页面使用的子资源。

例如

js
<link rel="prefetch" href="main.js" />

预取可用于获取可能进行的下一次导航的 HTML 和子资源。一个常见的用例是拥有一个简单的网站登录页面,该页面获取网站其余部分使用的更多“重量级”资源。

html
<link rel="prefetch" href="/app/style.css" />
<link rel="prefetch" href="/landing-page" />

结果保存在磁盘上的 HTTP 缓存中。因此,它对于预取子资源很有用,即使当前页面未使用它们。您也可以使用它来预取用户可能访问的网站上的下一个文档。但是,因此您需要小心处理标头——例如,某些Cache-Control 标头可能会阻止预取(例如no-cacheno-store)。

许多浏览器现在实现了某种形式的缓存分区,这使得<link rel="prefetch">对于打算供不同顶级站点使用的资源毫无用处。这包括跨站点导航时的主文档。因此,例如,以下预取

html
<link rel="prefetch" href="https://news.example/article" />

将无法从https://aggregator.example/访问。

**注意:**<link rel="prefetch">在功能上等效于带有priority: "low"选项设置的fetch() 调用,只是前者通常具有更低的优先级,并且它将具有Sec-Purpose: prefetch标头设置在请求中。

**注意:**prefetch操作的获取请求会产生一个 HTTP 请求,其中包含 HTTP 标头Sec-Purpose: prefetch。服务器可能会使用此标头更改资源的缓存超时,或执行其他特殊处理。该请求还将包含值为emptySec-Fetch-Dest标头。请求中的Accept标头将与正常导航请求中使用的值匹配。这允许浏览器在导航后找到匹配的缓存资源。如果返回响应,则将其与请求一起缓存在 HTTP 缓存中。

**注意:**这项技术曾经仅在 Chrome 中可用,现在已弃用。您应该改用Speculation Rules API,它取代了此技术。

<link rel="prerender"> 为浏览器提供了一个提示,表明用户可能需要在下次导航时访问目标资源,因此浏览器可以通过预渲染资源来提升性能。prerender 用于未来的导航,仅限同站,因此对于多页面应用程序 (MPA) 比较有意义,而不适用于单页面应用程序 (SPA)。

例如

html
<link rel="prerender" href="/next-page" />

它会获取引用的文档,然后获取所有可静态查找的链接资源并将其也获取,并将结果存储在磁盘上的 HTTP 缓存中,并设置 5 分钟的超时时间。例外情况是通过 JavaScript 加载的子资源——它不会查找这些资源。它还有其他问题——例如 <link rel="prefetch">,它也可能被 Cache-Control 头部阻止,并且由于浏览器 缓存分区,对于不同顶级站点使用的资源来说毫无用处。

推测规则 API

使用 推测规则 API 来指定一组规则,这些规则决定浏览器应该预取或预渲染哪些未来的文档。这些规则以 JSON 结构的形式提供,位于内联 <script type="speculationrules"> 元素和由 Speculation-Rules 响应头引用的外部文本文件中。

何时应该使用每个功能?

下表总结了上述功能,并提供了何时使用每个功能的指导。

预加载功能 用途 何时使用
<link rel="preconnect"> 跨源连接预热 在您最重要的跨源连接上使用,以在连接到它们时提供性能改进。
<link rel="dns-prefetch"> 跨源连接预热 在所有跨源连接上使用,以在连接到它们时提供少量性能改进。
<link rel="preload"> 当前页面子资源的高优先级加载 用于更快地加载当前页面上的高优先级资源,以实现战略性的性能改进。不要预加载所有内容,否则您将看不到好处。它还有一些其他有趣的用途——请参阅 Smashing Magazine (2016) 上的 预加载:它有什么用?
<link rel="modulepreload"> 当前页面 JavaScript 模块的高优先级加载 用于预加载高优先级 JavaScript 模块,以实现战略性的性能改进。
<link rel="prefetch"> 预填充 HTTP 缓存 用于预取同站点的未来导航资源或这些页面上使用的子资源。使用 HTTP 缓存,因此文档预取存在一些问题,例如可能被 Cache-Control 头部阻止。请改用 推测规则 API 进行文档预取,在支持的情况下。
<link rel="prerender"> 准备下一次导航 已弃用;建议您不要使用此功能。请改用 推测规则 API 预渲染,在支持的情况下。
推测规则 API 预取 准备下一次导航 用于预取相同或跨站点的未来导航文档。建议广泛采用,在支持的情况下;请确保这些页面 安全可预取。它不处理子资源预取;为此,您需要使用 <link rel="prefetch">
推测规则 API 预渲染 准备下一次导航 用于预加载同源的未来导航资源,以实现近乎即时的导航。在支持的情况下,将其用于高优先级页面;请确保这些页面 安全可预渲染

另请参阅