使用 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 脚本的优先级,使用比当前最常见的黑客行为更好的语义(为 async 脚本插入 <link rel="preload">)。
  • 降低后半部分脚本的优先级,以便更好地按顺序显示图片。
显示比较 Google 机票首页的两项测试的幻灯影片视图。在底部,“提取优先级”用于提升主打图片的优先级,从而使 LCP 减少 0.7 秒。
在 Google 机票测试中,提取优先级将 Largest Contentful Paint 从 2.6 秒缩短到 1.9 秒。

过去,使用预加载预连接时,开发者对资源优先级的影响有限。借助预加载,您可以在浏览器自然发现之前告知浏览器您想要提前加载的关键资源。这对于难以发现的资源(例如样式表中包含的字体、背景图片或通过脚本加载的资源)特别有用。预连接有助于预热与跨源服务器的连接,还有助于改进首字节时间等指标。如果您知道来源,但不一定知道所需资源的确切网址,那么该工具会很有用。

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

资源优先级

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

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

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

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

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

Chrome 开发者工具的“Network”标签页列出了大量字体资源。它们均为“最高优先级”。
BBC 新闻详情页面上资源 type = "font" 的优先级
Chrome 开发者工具的“Network”标签页列出了大量字体资源。混合了“低”和“高”优先级。
BBC 新闻详情页面上资源 type = "script" 的优先级。

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

Chrome 开发者工具的“Network”标签页。系统会勾选“Big request rows”(大请求行)设置,“Priority”(优先级)列会显示第一张优先级为“高”的图片,其下方会显示优先级为“高”的其他图片。提示中也会显示相同的内容。
开发者工具中的优先级更改。

在什么情况下您可能需要提取优先级?

现在您已了解浏览器的优先级逻辑,接下来可以调整网页的下载顺序,以优化其性能和核心网页指标。下面列举了一些示例来说明您可以更改哪些项目来影响资源下载的优先级:

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

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

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

  • 您有多张首屏图片,但不是所有图片都具有相同的优先级。例如,在图片轮播界面中,只有第一张可见图片的优先级较高,而其他图片(通常在屏幕外)的初始优先级较低。
  • 视口内的图像通常从 Low 优先级开始。布局完成后,Chrome 会发现它们位于视口中,并提升它们的优先级。这通常会导致关键图片(如主打图片)的加载出现明显的延迟。在标记中提供提取优先级可让图片从 High 优先级开始,并更早开始加载。为了在一定程度上实现自动化,Chrome 将前 5 张较大的图片的优先级设为 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 文档的形式提供)。

  在布局阻塞阶段加载 在布局阻塞阶段,一次加载一个
闪烁
优先级
VeryHigh 中等 VeryLow
开发者工具
优先级
最高 中等 最低
主要资源
CSS(早期**) ⬆◉
CSS(延迟**)
CSS(媒体不匹配***) ⬆*** ◉⬇
脚本(早期** 或并非来自预加载扫描器) ⬆◉
脚本(延迟**)
脚本(异步/延迟) ◉⬇
字体
字体 (rel=preload) ⬆◉
导入
图片(在视口中 - 布局之后) ⬆◉
图片(前 5 张图片 > 10,000px2)
映像 ◉⬇
媒体(视频/音频)
XHR(同步)- 已弃用
XHR/fetch*(异步) ⬆◉
预取
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 属性是一个提示,而不是指令。浏览器会尽量遵循开发者的偏好设置,但也可以应用其资源优先级偏好设置,以解决冲突。
  • 请勿将“提取优先级”与“预加载”混淆:

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

    “提取优先级”可以通过提高优先级的粒度来对预加载进行补充。如果您已在 <head> 中为某个 LCP 图片的前一项指定预加载,那么 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 提取优先级称为。

总结

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