使用 Fetch Priority API 优化资源加载

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 秒。
提取优先级信息,改进 Largest Contentful Paint:在 Google 机票测试中从 2.6 秒缩短到了 1.9 秒。

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

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

资源优先级

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

  • 资源类型,例如 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”(优先级)列会显示第一张优先级为“高”的图片,其下方会显示优先级为“高”的其他图片。提示中也会显示相同的内容。
开发者工具中的优先级更改。

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

现在您已了解浏览器的优先级逻辑,接下来可以调整网页的下载顺序以优化其性能和核心网页指标。下面列举了一些示例来说明在不使用“提取优先级”的情况下您可以进行的更改:

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

以下是一些更复杂的情况,在这些用例中,“提取优先级”可帮助您获得所需的资源优先级顺序:

  • 您有多个首屏图片,但并非所有图片都应具有相同的优先级。例如,在图片轮播界面中,只有第一张可见图片需要更高的优先级。
  • 视口内的主打图片通常从 LowMedium 优先级开始。布局完成后,Chrome 会发现它们位于视口中,因此会提高其优先级。这通常会显著增加图片的加载延迟。在标记中提供提取优先级可让图片以“高”优先级开始加载,并更早开始加载。

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

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" (◉) 表示该类型资源的默认优先级。

  在布局阻塞阶段加载 在布局阻塞阶段,一次加载一个
闪烁
优先级
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>

在早期针对 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'">

调整脚本的优先级

需要用于互动的网页脚本必须快速加载,但不应阻止其他资源。您可以将它们标记为“async”高优先级。

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

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

<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 提取优先级可能不会显著改善 LCP。不过,如果预加载发生在其他资源加载之后,high 提取优先级可以进一步改善 LCP。如果关键图片是 CSS 背景图片,请使用 fetchpriority = "high" 预加载该图片。

  • 在会有更多资源争用可用网络带宽的环境中,通过优先确定优先级来缩短加载时间会更加重要。这通常适用于无法并行下载的 HTTP/1.x 连接,或低带宽 HTTP/2 连接。在这些情况下,优先级设置可以帮助解决瓶颈。

  • CDN 未统一实现 HTTP/2 优先级。即使浏览器通过“提取优先级”传达优先级,CDN 可能不会按指定顺序重新确定资源的优先级。这使得提取优先级测试变得困难。优先级在浏览器内部以及支持优先级的协议(HTTP/2 和 HTTP/3)中应用。与 CDN 或源站支持无关的内部浏览器优先级设置仍值得单独使用提取优先级,因为当浏览器请求资源时,优先级通常会发生变化。例如,当浏览器处理关键的 <head> 项时,图片等低优先级资源通常会拒绝请求。

  • 您可能无法在初始设计中引入“提取优先级”作为最佳实践。在开发周期的后期,您可以将优先级分配给页面上的不同资源,如果它们与您的预期不符,您可以引入提取优先级以进行进一步优化。

关于使用预加载项的提示

使用预加载时,请注意以下几点:

  • 在 HTTP 标头中添加预加载项会将其置于加载顺序中的所有其他项之前。
  • 通常,对于优先级高于“中”的任何内容,预加载会按照解析器获取的顺序进行加载。在 HTML 开头添加预加载项,请务必小心。
  • 字体预加载可能在靠近头部或正文开头的位置效果最佳。
  • 导入预加载项(动态 import()modulepreload)应该在需要导入的脚本标记之后运行,因此请确保先加载或解析该脚本,以便在加载其依赖项时对其进行评估。
  • 默认情况下,图片预加载的优先级为“低”或“中”。请参照异步脚本以及其他低优先级或优先级最低的代码对它们进行排序。

历史记录

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