使用 Fetch Priority API 优化资源加载

Fetch Priority API 可指示资源相对于浏览器的优先级。它可以实现最佳加载和改进核心网页指标。

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

浏览器支持

  • Chrome:102。
  • Edge:102。
  • Firefox:132。
  • Safari:17.2.

来源

当浏览器解析网页并开始发现和下载图片、脚本或 CSS 等资源时,会为其分配提取 priority,以便以最佳顺序下载这些资源。资源的优先级通常取决于其内容和在文档中的位置。例如,视口内图片的优先级可能为 High,而对于在 <head> 中使用 <link> 的提前加载、会阻塞渲染的 CSS,其优先级可能是 Very High。浏览器非常擅长分配效果良好的优先级,但在所有情况下都可能不是最佳优先级。

本页介绍了 Fetch Priority API 和 fetchpriority HTML 属性,可让您提示资源的相对优先级(highlow)。Fetch Priority 有助于优化 Core Web Vitals。

摘要

“提取优先级”可在以下几个关键方面提供帮助

  • 通过在图片元素上指定 fetchpriority="high" 来提高 LCP 图片的优先级,从而更早地触发 LCP 图片。
  • 提高 async 脚本的优先级,使用的语义比当前最常见的黑客攻击(为 async 脚本插入 <link rel="preload">)更好。
  • 降低后部正文脚本的优先级,以便更好地对图片进行排序。
幻灯片视图,比较 Google 机票首页的两项测试。在底部,“提取优先级”用于提升主打图片的优先级,从而使 LCP 时间缩短 0.7 秒。
在 Google 机票测试中,抓取优先级将 Largest Contentful Paint 从 2.6 秒缩短到了 1.9 秒。

过去,开发者使用 preloadpreconnect 对资源优先级的影响有限。借助预加载,您可以在浏览器自然发现的关键资源之前提前加载这些资源。这对于难以发现的资源尤其有用,例如样式表中包含的字体、背景图片或从脚本加载的资源。预连接有助于预热到跨源服务器的连接,并有助于提升首次收到字节所用时间等指标。当您知道来源,但不一定知道所需资源的确切网址时,这会非常有用。

提取优先级是对这些资源提示的补充。它是基于标记的信号,通过 fetchpriority 属性提供,开发者可以使用它来指示特定资源的相对优先级。您还可以通过 JavaScript 和 Fetch API(具有 priority 属性)使用这些提示,以影响为数据提取资源的优先级。提取优先级也可以作为预加载的补充。取一个 LCP 图片,即使预加载,其优先级仍会较低。如果图片被其他早期的低优先级资源推回,则使用“提取优先级”功能有助于图片快速加载。

资源优先级

资源下载顺序取决于浏览器为网页上的每种资源指定的优先级。可能影响优先级计算逻辑的因素包括:

  • 资源的类型,例如 CSS、字体、脚本、图片和第三方资源。
  • 文档引用资源的位置或顺序。
  • 脚本是否使用了 asyncdefer 属性。

下表显示了 Chrome 如何确定大多数资源的优先级和排序:

  在布局阻塞阶段加载 在布局阻塞阶段逐个加载
Blink
优先级
非常高 中等 VeryLow
开发者工具
优先事项
最高 中等 最低
主要资源
CSS(早期**) CSS(延迟**) CSS(媒体不匹配***)
脚本(提前**或非从预加载扫描器加载) 脚本(延迟**) 脚本(异步)
字体 字体 (rel=preload)
导入
图片(在视口中) 图片(前 5 张图片 > 10,000 像素2) Image
媒体(视频/音频)
预取
XSL
XHR(同步) XHR/提取*(异步)

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

Chrome 开发者工具中的“Network”标签页,其中列出了多个字体资源。它们都是最高优先级。
BBC 新闻详情页面上资源 type = "font" 的优先级
Chrome 开发者工具的“Network”(网络)标签页,其中列出了多个字体资源。它们混合了低优先级和高优先级。
BBC 新闻详情页面上资源 type = "script" 的优先级

优先级发生变化时,您可以在大型请求行设置或提示中同时查看初始优先级和最终优先级。

Chrome 开发者工具的“Network”(网络)标签页。“大请求行”设置已选中,并且“优先级”列显示第一个图片的优先级为“高”,下面的另一个图片的初始优先级为“中”。提示中也会显示相同的信息。
DevTools 中的优先级变更。

何时可能需要提取优先级?

现在,您已经了解了浏览器的优先次序逻辑,接下来可以通过调整网页的下载顺序来优化其性能和核心网页指标了。以下示例展示了您可以通过更改来影响资源下载的优先级:

  • 按照您希望浏览器下载资源的顺序放置资源标记(例如 <script><link>)。具有相同优先级的资源通常会按发现的顺序加载。
  • 使用 preload 资源提示尽早下载必要的资源,尤其是对于浏览器不易发现的资源。
  • 使用 asyncdefer 下载脚本,而不会屏蔽其他资源。
  • 延迟加载首屏线以下的内容,以便浏览器可以将可用带宽用于更关键的首屏线以上资源。

这些方法有助于控制浏览器的优先级计算,从而提升性能和核心网页指标。例如,如果预加载了某个关键背景图片,系统可以更早地发现它,从而改进了 Largest Contentful Paint (LCP)。

有时,这些句柄可能不足以为您的应用最佳地确定资源优先级。以下是“提取优先级”功能可能对您有所帮助的一些场景:

  • 您有多个首屏图片,但它们的优先级不应完全相同。例如,在图片轮播界面中,只有第一张可见图片需要更高的优先级,而其他(通常在屏幕外)图片最初可以设置为具有较低的优先级。
  • 视口内的图像通常从 Low 优先级开始。布局完成后,Chrome 会发现它们位于视口内,并提高其优先级。这通常会显著增加加载关键图片(例如主打图片)的延迟。在标记中提供提取优先级可让图片以 High 优先级启动,并提前加载。为了尽量实现自动化,Chrome 会将前五张较大的图片设为 Medium 优先级,这会有所帮助,但明确的 fetchpriority="high" 会更好。

    若要及早发现添加为 CSS 背景的 LCP 图片,仍需要预加载。如需提高背景图片的优先级,请在预加载中添加 fetchpriority='high'
  • 将脚本声明为 asyncdefer 即指示浏览器异步加载它们。不过,正如优先级表格中所示,系统还会为这些脚本分配“低”优先级。您不妨提高这些脚本的优先级,同时确保可以异步下载,尤其是对于对用户体验至关重要的脚本。
  • 如果您使用 JavaScript fetch() API 异步提取资源或数据,浏览器会为其分配 High 优先级。您可能希望以较低优先级运行某些提取,尤其是在您将后台 API 调用与响应用户输入的 API 调用混合使用时。将后台 API 调用标记为 Low 优先级,将交互式 API 调用标记为 High 优先级。
  • 浏览器会为 CSS 和字体分配 High 优先级,但其中一些资源可能比其他资源更重要。您可以使用“提取优先级”来降低非关键资源的优先级(请注意,早期的 CSS 会阻塞渲染,因此通常应设为 High 优先级)。

fetchpriority 属性

使用 fetchpriority HTML 属性,为使用 linkimgscript 标记下载的资源(例如 CSS、字体、脚本和图片)指定下载优先级。它可以采用以下值:

  • 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 文档的形式提供)。

  在布局阻塞阶段加载 在布局阻塞阶段一次加载一个
Blink
优先级
非常高 中等 VeryLow
开发者工具
优先事项
最高 中等 最低
主要资源
CSS(早期**) ⬆◉
CSS(延迟**)
CSS(媒体不匹配***) ⬆*** ◉⬇
脚本(提前**或非从预加载扫描器加载) ⬆◉
脚本(延迟**)
脚本(异步/延迟) ◉⬇
字体
字体(rel=preload) ⬆◉
导入
图片(视口中 - 布局后) ⬆◉
图片(前 5 张图片 > 10,000 像素2)
Image ◉⬇
媒体(视频/音频)
XHR(同步)- 已废弃
XHR/提取*(异步) ⬆◉
预取
XSL

fetchpriority 会设置相对优先级,这意味着它会适当提高或降低默认优先级,而不是将优先级明确设置为 HighLow。这通常会导致 HighLow 优先级,但并不总是如此。例如,fetchpriority="high" 的关键 CSS 会保留“非常高”/“最高”优先级,而对这些元素使用 fetchpriority="low" 则会保留“高”优先级。这两种情况都不涉及将优先级明确设置为 HighLow

使用场景

如果您想为浏览器提供关于使用什么优先级提取资源的额外提示,请使用 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>

虽然图片 2-4 将不在视口范围内,但它们可能会被视为“足够近”,可以提升到 high,并且即使添加了 load=lazy 属性也会加载。因此,fetchpriority="low" 是正确的解决方案。

在之前针对 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" as="script" href="non-critical-script.js" fetchpriority="low">

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

重新排定脚本的优先级

网页需要的脚本应快速加载,但不应阻塞其他更重要的呈现阻塞资源。您可以将这类应用标记为async,并为其设置高优先级。

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

如果脚本依赖于特定 DOM 状态,则无法将其标记为 async。但是,如果它们稍后在页面上运行,您可按照较低的优先级加载它们:

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

这仍会在到达此脚本时阻止解析器,但允许优先处理此脚本之前的内容。

如果需要已完成的 DOM,另一种方法是使用 defer 属性(该属性在 DOMContentLoaded 之后依次运行),甚至使用页面底部的 async

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

浏览器会以高优先级执行 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 属性是一个提示,而非指令。浏览器会尝试遵循开发者的偏好设置,但也可以为资源优先级应用资源优先级偏好设置来解决冲突。
  • 不要将“提取优先级”与“预加载”混淆:

    • 预加载是一项强制性提取操作,而不是提示。
    • 通过预加载,浏览器可以提前发现资源,但仍会以默认优先级提取资源。反之,提取优先级对可检测性没有帮助,但可以让您提高或降低提取优先级。
    • 与优先级更改的影响相比,预加载的影响通常更容易观察和衡量。

    提取优先级可以通过提高优先级的粒度来补充预加载。如果您已将预加载指定为 LCP 图片的 <head> 中的第一个项,则 high 提取优先级可能不会显著缩短 LCP 用时。但是,如果预加载发生在其他资源加载之后,high 提取优先级可以进一步提高 LCP。如果关键图片是 CSS 背景图片,请使用 fetchpriority = "high" 预加载该图片。

  • 在更多资源争夺可用网络带宽的环境中,优先级功能带来的加载时间缩短效果更为明显。对于无法并行下载的 HTTP/1.x 连接,或者带宽较低的 HTTP/2 或 HTTP/3 连接,这种情况很常见。在这些情况下,确定优先级有助于解决瓶颈问题。

  • CDN 不会以相同的方式实现 HTTP/2 优先级,对于 HTTP/3 也是如此。即使浏览器通过提取优先级来传达优先级,CDN 可能不会按指定顺序重新设定资源的优先级。这使得测试“抓取优先级”变得困难。优先级既适用于浏览器内部,也适用于支持优先级的协议(HTTP/2 和 HTTP/3)。但对于与 CDN 或源站支持无关的内部浏览器优先级设置,您还是可以使用“提取优先级”功能,因为当浏览器请求资源时,优先级通常会改变。例如,在浏览器处理关键 <head> 项时,系统通常会阻止请求图片等低优先级资源。

  • 您可能无法在最初的设计中引入提取优先级作为最佳做法。在开发周期的后期,您可以为网页上的不同资源分配优先级,如果这些优先级与您的预期不符,您可以引入提取优先级以进行进一步优化。

开发者应将预加载用于其预期用途,即预加载解析器未检测到的资源(字体、导入内容、背景 LCP 图片)。preload 提示的位置会影响资源的预加载时间。

提取优先级决定着在提取资源时应如何提取资源。

关于使用预加载的提示

使用预加载时,请注意以下事项:

  • 在 HTTP 标头中添加预加载项会将其放在加载顺序中的所有其他内容之前。
  • 通常,对于优先级为 Medium 或更高的任何内容,预加载项会按解析器获取它们的顺序加载。如果您要在 HTML 的开头添加预加载项,请务必谨慎。
  • 字体预加载可能在字体的末尾或正文的开头效果最佳。
  • 导入预加载项(动态 import()modulepreload)应在需要导入的脚本代码之后运行,因此请确保先加载或解析脚本,以便在其依赖项加载时对其进行评估。
  • 默认情况下,图片预加载的优先级为 LowMedium。将它们相对于异步脚本和其他低优先级或最低优先级的代码进行排序。

历史记录

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

总结

随着预加载行为的修复以及近期对 Core Web Vitals 和 LCP 的关注,开发者可能会对提取优先级感兴趣。现在,它们具有额外的旋钮,可用于实现首选的加载序列。