使用 Fetch Priority API 优化资源加载

Fetch Priority API 指示了资源相对于浏览器的优先级。它可以实现最佳加载效果并改进 Core Web Vitals。

Addy Osmani
Addy Osmani
Leena Sohoni
Leena Sohoni
Patrick Meenan
Patrick Meenan

浏览器支持

  • 102
  • 102
  • x
  • 17.2

来源

当浏览器解析网页并开始发现和下载资源(例如图片、脚本或 CSS)时,会为其分配提取 priority,尝试按最佳顺序下载资源。这些优先级可能取决于资源类型及其在文档中的位置。例如,视口内图片的优先级可能为 High,而通过 <head> 中的 <link> 会阻塞过早加载且阻止呈现的 CSS 的优先级为 Very High。浏览器非常擅长分配虽然行之有效但并非在所有情况下都达到最佳的优先级。

本文介绍了 Fetch Priority API 和 fetchpriority HTML 属性,您可以利用这些属性提示资源的相对优先级(highlow)。“提取优先级”有助于优化核心网页指标。

摘要

“抓取优先级”可以在以下几个关键方面发挥作用

  • 通过在图片元素上指定 fetchpriority="high" 来提高 LCP 图片的优先级,以便更快执行 LCP。
  • 使用比当前常用技巧(为 async 脚本插入 <link rel="preload">)更好的语义提高 async 脚本的优先级。
  • 降低后期正文脚本的优先级,以便更好地按顺序排列图片。
显示比较 Google 机票首页的两项测试的幻灯影片视图。在底部,“提取优先级”可用于提升主打图片的优先级,从而使 LCP 减少 0.7 秒。
在 Google 机票测试中,提取优先级将 Largest Contentful Paint 从 2.6 秒缩短到 1.9 秒

过去,开发者使用预加载预连接可对资源优先级施加一定程度的影响,但影响有限。“提取优先级”是对这些资源提示的补充,但了解它们的适用情形至关重要。借助预加载,您可以告知浏览器您希望在关键资源被自然发现之前尽早加载的资源。这对于不容易发现的资源(例如样式表中包含的字体、背景图片或通过脚本加载的资源)特别有用。预连接有助于预热与跨源服务器的连接,并且有助于改进Time-to-first-byte等指标。如果您知道源,但不一定是所需资源的确切网址,则预连接非常有用。

“提取优先级”是一种基于标记的信号(通过 fetchpriority 属性提供),开发者可以用它来指示特定资源的相对优先级。您还可以通过 JavaScript 和 Fetch APIpriority 属性使用这些提示,以影响针对数据进行的资源提取的优先级。提取优先级也可以补充预加载。获取 Largest Contentful Paint 图片,此类图片在预加载时仍会获得较低的优先级。如果其他早期低优先级资源将其推送回来,使用“提取优先级”可帮助您尽快加载图片。

“抓取优先级”在 Chrome 101 或更高版本中提供

资源优先级

资源下载顺序取决于浏览器为网页上的每项资源分配的优先级。不同因素可能会影响优先级计算逻辑。例如,

  • 系统会为 CSS、字体、脚本、图片和第三方资源分配不同的优先级。
  • 在文档中引用资源的位置或顺序也会影响资源的优先级。
  • preload 资源提示有助于浏览器更快地发现资源,从而在文档加载该资源并影响优先级之前加载该资源。
  • asyncdefer 脚本的优先级计算更改。

下表考虑了此类因素,显示大多数资源目前在 Chrome 中的优先级和排序方式。

  在布局阻塞阶段加载 在布局阻塞阶段一次加载 1 个
闪烁
优先级
VeryHigh 中等 VeryLow
开发者工具
优先级
最高 中等 最低
主要资源
CSS(早期**) CSS(延迟**) CSS(媒体不匹配***)
脚本(早期** 或并非来自预加载扫描器) 脚本(延迟**) 脚本(异步)
字体 字体 (rel=preload)
导入
图片(在视口中) 图片(前 5 张图片 > 10,000px2) 映像
媒体(视频/音频)
预取
XSL
XHR(同步) XHR/fetch*(异步)

浏览器会按照发现的顺序下载具有相同计算优先级的资源。在 Chrome 开发者工具的网络标签页下,您可以查看加载页面时分配给不同资源的优先级。(确保通过右键点击表格标题来包含优先级列)。

Chrome 开发者工具的“网络”标签页中列出的资源的屏幕截图。从左到右依次为:名称、状态、类型、发起方、规模、时间和优先级。
BBC 新闻详情页面上资源 type = "font" 的优先级
Chrome 开发者工具的“网络”标签页中列出的资源的屏幕截图。从左到右依次为:名称、状态、类型、发起方、规模、时间和优先级。
BBC 新闻详情页面中资源类型的优先级 =“script”

如果优先级发生变化,您可以使用大请求行设置来查看初始优先级和最终优先级。无论大请求行设置为何,提示中都会显示相同的内容。

Chrome 开发者工具的“网络”标签页中列出的资源的屏幕截图。系统会勾选“Big request rows”(大请求行)设置,“Priority”(优先级)列会显示第一张优先级为“高”的图片,其下方会显示优先级为“高”的其他图片。提示中也会显示相同的内容。
在开发者工具中看到初始优先级和最终优先级

在什么情况下需要使用“提取优先级”?

了解浏览器的优先级逻辑,您就可以使用一些现有的旋钮来调整下载的顺序。您可以

  1. 根据下载顺序,放置 <script><link> 等资源标记。优先级相同的资源通常按照发现的顺序进行加载。
  2. 使用 preload 资源提示尽早下载必要的资源,尤其是对于浏览器不易发现的资源。
  3. 使用 asyncdefer 下载脚本,且不会屏蔽其他资源。
  4. 延迟加载非首屏内容,以便浏览器可以将可用带宽用于更重要的首屏资源。

这些技术有助于控制浏览器的优先级计算,从而提升性能和核心网页指标。例如,预加载关键背景图片时,可以更早发现此类图片,从而改进 Largest Contentful Paint (LCP)。

有时,这些句柄可能不足以为您的应用以最佳方式优化资源优先级。下面列举了一些可能对提取优先级有所帮助的情况:

  1. 您有多个首屏图片,但它们的优先级不必相同。例如,在图片轮播界面中,只有第一张可见图片需要具有比其他图片更高的优先级。
  2. 视口内的主打图片通常从“低”优先级开始(请注意,Chrome 117 中的更改会将前 5 张大图片设为“中”,但可能会包含主打图片,也可能不包含)。布局完成后,Chrome 会发现它们位于视口中,并提升它们的优先级。这通常会显著增加图片的加载延迟。在标记中提供提取优先级可让图片以“高”优先级开始加载,尽早开始加载。

    请注意,若要尽早发现作为 CSS 背景包含的 LCP 图片,仍需要预加载。您可以通过在预加载项中添加 fetchpriority='high' 与“提取优先级”结合使用,否则图片仍会从“低”或“中”优先级开始加载。
  3. 将脚本声明为 asyncdefer 会指示浏览器异步加载这些脚本。不过,如上表所示,系统也为这些脚本分配了“低”优先级。您可能希望提高其优先级,同时确保异步下载,尤其是对于对用户体验至关重要的任何脚本。
  4. 您可以使用 JavaScript fetch() API 异步提取资源或数据。浏览器为抓取操作指定了“高”优先级。在某些情况下,您可能不希望以“高”优先级执行所有抓取操作,而是希望改用其他抓取优先级。在运行后台 API 调用并将其与响应用户输入(例如使用自动补全)的 API 调用混合使用时,此方法会很有用。后台 API 调用可被标记为“低”优先级,而交互式 API 调用可被标记为“高”优先级。
  5. 浏览器会为 CSS 和字体分配“高”优先级,但对于 LCP 而言,所有此类资源可能并不同样重要,也并非必需的资源。您可以使用提取优先级来降低其中一些资源的优先级。

fetchpriority 属性

您可以使用 fetchpriority HTML 属性提供提取优先级。您可以将该属性与 linkimgscript 标记一起使用。通过此属性,您可以为使用支持的标记下载的资源(例如 CSS、字体、脚本和图片)指定优先级。 fetchpriority 属性接受以下三个值之一:

  • high:您认为该资源具有高优先级,并且希望浏览器优先考虑该资源,前提是浏览器的启发式算法不会阻止这种情况的发生。
  • low:您认为该资源的优先级较低,并希望浏览器在启发式算法允许的情况下降低该资源的优先级。
  • auto:这是您没有偏好的默认值,让浏览器可以决定适当的优先级。

以下是在标记中使用 fetchpriority 属性以及与脚本等效的 priority 属性的一些示例。

<!-- We don't want a high priority for this above-the-fold image -->
<img src="/images/in_viewport_but_not_important.svg" fetchpriority="low" alt="I'm an unimportant image!">

<!-- We want to initiate an early fetch for a resource, but also deprioritize it -->
<link rel="preload" href="/js/script.js" as="script" fetchpriority="low">

<script>
  fetch('https://example.com/', {priority: 'low'})
  .then(data => {
    // Trigger a low priority fetch
  });
</script>

浏览器优先级和fetchpriority

您可以将 fetchpriority 属性应用于不同的资源(如下图所示),以便可能会提高或降低其计算优先级。每行中的 fetchpriority="auto" (◉) 表示该类型资源(以 Google 文档的形式提供)的默认优先级。

  在布局阻塞阶段加载 在布局阻塞阶段一次加载 1 个
闪烁
优先级
VeryHigh 中等 VeryLow
开发者工具
优先级
最高 中等 最低
主要资源
CSS(早期**) ⬆◉
CSS(延迟**)
CSS(媒体不匹配***) ⬆*** ◉⬇
脚本(早期** 或并非来自预加载扫描器) ⬆◉
脚本(延迟**)
脚本(异步/延迟) ◉⬇
字体
字体 (rel=preload) ⬆◉
导入
图片(在视口中 - 布局之后) ⬆◉
图片(前 5 张图片 > 10,000px2)
映像 ◉⬇
媒体(视频/音频)
XHR(同步)- 已弃用
XHR/fetch*(异步) ⬆◉
预取
XSL

请注意,fetchpriority 会设置相对优先级,也就是说,它会将默认优先级调高或调低适当的值,而不是将优先级显式设置为“高”或“低”,然后浏览器来决定相对优先级。通常值为“高”或“不理想”,但并不总是如此。例如,具有 fetchpriority="high" 的关键 CSS 仍将保留“非常高”/“高”优先级,而对这些 CSS 使用 fetchpriority="low" 时,将仍保留“高”优先级,无论哪种情况下优先级均未明确设置为“高”或“低”。

用例

您可以使用 fetchpriority 属性来应对您可能希望为浏览器提供额外提示,告知系统应以什么优先级提取资源的情况。

提高 LCP 图片的优先级

您可以指定 fetchpriority="high" 来提升 LCP 或其他关键映像的优先级。

<img src="lcp-image.jpg" fetchpriority="high">

以下对比显示了在加载和不使用“提取优先级”的情况下加载的 LCP 背景图片的 Google 机票页面。将优先级设置为“高”后,LCP 时间从 2.6 秒缩短到了 1.9 秒

使用 Cloudflare 工作器重新编写了 Google 机票页面,以使用“提取优先级”进行实验。

降低首屏图片的优先级

您可以使用 fetchpriority="low" 降低最初可能不重要的首屏图片的优先级,例如在图片轮播界面中。

<ul class="carousel">
  <img src="img/carousel-1.jpg" fetchpriority="high">
  <img src="img/carousel-2.jpg" fetchpriority="low">
  <img src="img/carousel-3.jpg" fetchpriority="low">
  <img src="img/carousel-4.jpg" fetchpriority="low">
</ul>

在早期针对 Oodle 应用的实验中,我们使用了它来降低加载时不显示的图片的优先级。帮助将加载时间缩短了 2 秒。

并排比较了在 Oodle 应用的图片轮播界面中使用时的提取优先级。在左侧,浏览器为轮播图片设置默认优先级,但下载和绘制这些图片的速度会比右侧示例慢约 2 秒,这只是为第一张轮播图片设置了更高的优先级。

降低预加载资源的优先级

为防止预加载资源与其他关键资源竞争,您可以提供降低其优先级的提示。您可以将此方法用于图片、脚本和 CSS。

<!-- Lower priority only for non-critical preloaded scripts -->
<link rel="preload" as="script" href="critical-script.js">
<link rel="preload" href="/js/script.js" as="script" fetchpriority="low">

<!-- Preload CSS without blocking other resources -->
<link rel="preload" as="style" href="theme.css" fetchpriority="low" onload="this.rel='stylesheet'">

调整脚本的优先级

需要让网页的某些部分具有可交互性所需的脚本是必不可少的,但不应阻止其他资源。您可以将这些依赖项标记为异步并设为高优先级。

<script src="async_but_important.js" async fetchpriority="high"></script>

如果脚本依赖于特定的 DOM 状态,则无法将其标记为异步。但是,如果它们位于页面的最下方,则可以按照较低的优先级下载它们,如下所示。

<script src="blocking_but_unimportant.js" fetchpriority="low"></script>

降低非关键数据提取的优先级

浏览器以高优先级执行 fetch。如果您有多个可同时触发的提取操作,则可以为更重要的数据提取使用高默认优先级,而为不太重要的数据使用较低的默认优先级。

// Important validation data (high by default)
let authenticate = await fetch('/user');

// Less important content data (suggested low)
let suggestedContent = await fetch('/content/suggested', {priority: 'low'});

提取优先级实现说明

如前所述,提取优先级可以提高特定用例中的性能。需要注意以下事项:

  • fetchpriority 属性是一个提示,而不是指令。浏览器会尽量遵循开发者的偏好。此外,在发生冲突时,浏览器也可能会根据需要应用其资源优先级偏好设置。
  • 请勿将“提取优先级”与“预加载”混淆。这两者之所以不同,原因如下:

    • 预加载是一项强制性提取操作,并非提示。
    • 预加载可让浏览器尽早发现资源,但仍会使用默认优先级提取资源。相反,“抓取优先级”不会提高其可检测性,但可让您提高或降低抓取优先级。
    • 更容易观察和衡量预加载的效果。

    “提取优先级”可以通过提高优先级的粒度来对预加载进行补充。如果您已在 <head> 中为某个 LCP 图片指定预加载项之一,那么 high 提取优先级可能不会带来显著提升。不过,如果预加载是在其他资源之后,那么 high 提取优先级可以提高 LCP。如果关键图片是 CSS 背景图片,您应使用 fetchpriority = "high" 预加载它。

  • 在更多资源争用可用网络带宽的环境中,优先级设置带来的显著收益更为相关。这常用于无法并行下载的 HTTP/1.x 连接,或低带宽 HTTP/2 连接。在此类情况下,确定优先级可以解决瓶颈问题。

  • CDN 不会统一地实现 HTTP/2 优先级。即使浏览器使用“提取优先级”传达建议的优先级,CDN 可能不会按所需顺序重新确定资源的优先级。这使得抓取优先级测试变得非常困难。浏览器内部以及支持优先级的协议(HTTP/2 和 HTTP/3)都会应用优先级。即使仅针对与 CDN 或源站支持无关的内部浏览器优先级设置,这种做法也很有价值,因为当浏览器请求资源时,这种优先级会经常发生变化。例如,当浏览器处理关键的 <head> 项时,通常会阻止请求图片等低优先级资源。

  • 在您的初始设计中,可能无法将“抓取优先级”作为最佳做法引入。这是一项可在开发周期后期运用的优化。您可以检查为网页上不同资源分配的优先级,如果它们与您的预期不符,您可以引入“提取优先级”,以便进一步优化。

在 Chrome 95 之后使用预加载功能

“抓取优先级”功能已在 Chrome 73 至 76 中供用户试用,但由于 Chrome 95 中的预加载项的优先级问题已修复,因此该功能未发布。在 Chrome 95 之前,通过 <link rel=preload> 发出的请求始终先于预加载扫描程序发现的其他请求开始,即使其他请求的优先级较高也是如此。

通过在 Chrome 95 中的修复程序和“提取优先级”增强功能,我们希望开发者能够开始使用预加载功能实现其预期用途,即预加载解析器未检测到的资源(字体、导入内容、后台 LCP 图片)。preload 提示的放置位置会影响资源预加载的时间。关于使用预加载的一些要点如下:

  • 在 HTTP 标头中包含预加载将使其跳到所有其他内容之前。
  • 一般来说,对于优先级高于“中”优先级的任何内容,预加载将按照解析器获取预加载的顺序进行加载,因此,在 HTML 开头包括预加载时要小心。
  • 字体预加载可能在靠近头部或正文末尾时效果最佳。
  • 导入预加载项(动态 import()modulepreload)应在需要导入的脚本标记之后完成(这样才能先加载/解析实际脚本)。基本上,如果脚本标记加载的脚本会触发加载依赖项,请确保依赖项的 <link rel=preload> 位于父脚本标记之后,否则依赖项可能在主脚本之前加载。以适当的顺序在加载依赖项时可以解析/评估主脚本。
  • 图片预加载具有“低”或“中”优先级(无提取优先级),应相对于异步脚本和其他优先级低或最低的代码进行排序。

历史记录

“提取优先级”功能于 2018 年在 Chrome 中首次进行源试用,并于 2021 年使用 importance 属性进行了实验。当时,它称为“优先级提示”。此后,作为网络标准流程的一部分,该接口已针对 HTML 更改为 fetchpriority,针对 JavaScript 的 Fetch API 更改为 priority。为减少混淆,我们现在将此 API 称为“提取优先级”。

总结

开发者可能会对“提取优先级”感兴趣,并修复了预加载行为方面的问题,并近期重点关注了核心网页指标和 LCP。现在,他们有了额外的旋钮,可以实现所需的加载顺序。