查看原文
其他

WEB性能:利用Priority Hints和fetchpriority提升资源加载速度

小懒 FED实验室 2024-02-12
关注下方公众号,获取更多热点资讯

如何让网站的资源加载更快?这个问题可能是网站所有者及开发者非常关心的话题。可能就那几毫秒,确会决定着网站的用户流失率、功能转化率及商业收益的多少。

当涉及到资源加载问题时,我们通常会有几种选择:

  • 完全依赖浏览器的机制,以最佳的顺序下载及获取资源
  • 通过预加载策略来提前加载资源
  • 通过资源的 Priority Hints 来提升加载速度

本文将通过一下几个方面深入介绍 Priority Hints及对应机制:

  • 什么是 Priority Hints?
  • 何时使用 Priority Hints?
  • 如何通过 fetchpriority 实现 Priority Hints?
  • 如何在你的网站上测试 Priority Hints?

1.什么是 Priority Hints?

Priority Hints 是一种信号,允许网站所有者和开发人员在浏览器加载资源(例如图片、字体、CSS、脚本和 iframe)时指示资源的优先级。

重要提示:Priority Hints 在执行过程中并非强制性的,就像 preload 属性一样。它们只是偏好提示,根据浏览器的情况来执行。这意味着内置浏览器的启发式算法可能会覆盖 Priority Hints。

我们知道,浏览器在构建网页的过程中需要下载各种资源。作为高优资源,浏览器首先会请求并下载 HTML 文档,那么浏览器如何确定下一个要加载的资源呢?

浏览器对于每种资源类型都有一组预定的优先级:

这些默认优先级通常都能很好地工作,从而实现良好的网络性能。

2.何时使用 Priority Hints?

Priority Hints 主要应用场景:

1)网页首屏有几张图片,但它们的优先级不必相同。例如,在图片轮播界面中,只有第一张可见图片需要比其他图片更高的优先级。

2)视口内的主图像初始优先级较低。在布局完成后,Chrome会发现它们在视口内并提升它们的优先级(不幸的是,开发工具只显示最终优先级,WebPageTest会同时显示两者)。这通常会导致加载图片的延迟。通过在标记中提供优先级提示,可以使图像以较高的优先级启动,并且可以更早开始加载。

请注意,若要尽早发现作为 CSS 背景包含的 LCP 图片,仍需进行预加载;若想与提取优先级结合使用,请在预加载中添加 fetchpriority='high',否则图片仍会以默认的“低”优先级开始加载。

3)将脚本声明为 async 或 defer 会指示浏览器异步加载这些脚本。不过,系统还会为这些脚本分配“低”优先级。您可能需要提高其优先级,同时确保异步下载,尤其是对于任何对用户体验至关重要的脚本。

4)您可以使用JavaScript的 fetch() API以异步方式获取资源或数据。浏览器会为fetch分配一个高优先级。在某些情况下,您可能不希望所有的fetch都在高优先级下执行,并且更喜欢使用不同的优先级提示。当运行后台API调用并将其与响应用户输入的API调用混合使用时,这将非常有用,比如自动完成。后台API调用可以标记为低优先级,而交互式API调用可以标记为高优先级。

5)浏览器会为 CSS 和字体分配“高”优先级,但所有这些资源对于 LCP 而言可能并不是同等重要或必需的。您可以使用“提取优先级”来降低其中某些资源的优先级。

几乎所有的网站都属于前两种情景。要确定是否属于其他情况,需要研读代码后才能判定。

3.如何通过 fetchpriority 实现 Priority Hints?

开发者可以使用 fetchpriority HTML 属性来实现优先级提示,可以在以下标签中使用:

  • link(链接)
  • img(图片)
  • script(脚本)
  • iframe(框架)

fetchpriority 属性接受以下三个值之一:

  • high:表示该资源很重要,希望浏览器优先处理它。
  • low:表示该资源不太重要,希望浏览器降低优先级处理它。
  • auto:默认值,标识没有偏好,让浏览器决定适当的优先级。

3.1.Preload 请求优先级

现代浏览器有一种支持良好的方法,可以告知当前页面最终需要的资源:<link rel="preload" .../>。如果将其放在文档的 <head>中,浏览器就会收到指示,以 "高" 优先级尽快开始下载。

不过,浏览器中的预加载扫描器在这类事情上已经做得非常好了(我以前写过一篇关于它的更深入的文章)。因此,预加载通常最适合用于加载非首屏资源,比如通过内联样式属性加载的背景图片,但它也适用于其他任何可能无法按照浏览器的要求进行优先排序的内容。

例如:默认情况下,Chrome 浏览器会以极高的优先级加载字体,但如果有人的网络连接速度较慢,它就会使用后备字体并降低优先级,通过 CSS @font-face 规则加载的字体:

@font-face {
 font-family"Inter Variable";
 srcurl("./font.woff2"format("woff2");
}

在加载时,虽然字体对页面的视觉体验相当重要,由于连接速度较慢,该字体的下载优先级最低:

但我们可以通过预加载该资源来干预浏览器的决定:

<head>
 <link rel="preload" href="/font.woff2" as="font">
</head>

我们看到优先级变为 high 了:

此外,你还可以直接在链接标签上使用 fetchpriority(获取优先级)来获得更明确的信息。它可以让你在一次预加载多个资源时设置相对优先级信号。

例如:你想预载两种字体,但其中一种字体的优先级高于另一种:

<link rel="preload" href="./font-1.woff2" as="font" fetchpriority="low" />
<link rel="preload" href="./font-2.woff2" as="font" fetchpriority="high" />

总的来说,当一个资源不是由你的 HTML 直接加载,但对于页面体验至关重要(例如字体、CSS背景图片等),使用预加载(preloading)是很好的选择。当预加载多个相同类型的资源,并且你明确了哪个是最重要的时,还应包括fetchpriority属性。

3.2.fetch 请求优先级

Fetch API 是现代 Web 中比较好用的 API 之一,它拥有 XMLHttpRequest 所缺乏的一些不错的功能,比如在发出请求时发出优先级信号的功能。

例如:当带宽不足且有多个请求时,浏览器会自行决定优先级。但作为开发工程师,普通请求应该让位于对页面目的更重要的其他请求:

下面的两个请求优先级基本一致:

fetch("http://localhost:8000/pay", {
 method"POST",
 body: paymentBody,
});

fetch("http://localhost:8000/log", {
 method"POST",
 body: loggingBody,
});

现在,我们要明确地告诉浏览器每个请求的优先级:

fetch("http://localhost:8000/pay", {
 method"POST",
 body: paymentBody,
  priority"high"
});

fetch("http://localhost:8000/log", {
 method"POST",
 body: loggingBody,
  priority"low"
});

当知道有多个请求正在并发执行,而且哪个请求最重要(或哪个请求可以安全地取消优先级)时,可以通过指明 fetch() 优先级来实现。

3.3.图片请求优先级

在我们不做任何特殊操作的情况下,浏览器会尽力确定页面上最重要的图片。为了说明这一点,我加载了以下图片,这些图片之间相隔很远,因此只有一张在首屏出现。

<img src="./cat-1.jpeg" />
<div style="height: 5000px"></div>
<img src="./cat-2.jpeg" />
<div style="height: 5000px"></div>
<img src="./cat-3.jpeg" />

浏览器能识别出哪个最重要,但这需要一秒钟的时间。下载开始时,三个页面的优先级都是 "低"。但没过多久,首屏图片的优先级变成了 "高"。

当我为第一张图片添加了 fetchpriority 属性后,情况就变得更容易预测了:

<img src="./cat-1.jpeg" fetchpriority="high"/>
<div style="height: 5000px"></div>
<img src="./cat-2.jpeg" loading="lazy" />
<div style="height: 5000px"></div>
<img src="./cat-3.jpeg" loading="lazy" />

有了这个功能,浏览器就能准确地知道如何(以及是否)加载图片,而且只在适当的时候加载。由于增加了 loading=“lazy”,它甚至不会在首次加载时开始请求屏幕外的图像。相反,它会等到它们更靠近视口时才开始。

当你知道图片对页面体验非常重要时,对图片使用明确的 fetchpriority。首屏甚至的加载快慢会对页面的核心网页要素产生影响,特别是 LCP(最大内容绘制时间)。

3.4.script 请求优先级

页面上任何带有 src 属性的纯 <script /> 都会在获取过程中获得高优先级,但这需要权衡利弊:在加载和执行之前,它会阻止页面其他部分的解析。因此 async 属性变得非常有用,它会在后台以较低的优先级请求脚本,并在准备就绪后立即执行。了解了这一点,下面的设置就会有可预见的表现:

<script src="/script-async.js" async onload="console.log('async')"></script>
<script src="/script-sync.js" onload="console.log('sync')"></script>
<script>console.log("inline");</script>

控制台确认,在异步脚本加载时,允许后续脚本解析和执行。

大多数情况下,这种行为没有问题。但有时,您可能希望脚本既能异步加载,又能以 "高" 优先级加载。我们给它一个 fetchpriority 属性:

<script src="/script-async.js" async onload="console.log('async')" fetchpriority="high"></script>
<script src="/script-sync.js" onload="console.log('sync')"></script>
<script>console.log("inline");</script>

现在,它的下载优先级提高了,同时仍不会阻塞页面的其他部分。控制台也验证了这一点。有了更高的优先级,异步脚本的加载速度会更快。在这种情况下,甚至比同步和内联脚本更快。

如果您事先知道脚本的优先级,而且怀疑浏览器没有足够的信息来自行决定,那么就在脚本上设置 fetchpriority 属性。正如我所提到的,它对于确定脚本的优先级特别有帮助,因为你也希望以非阻塞、异步的方式加载这些脚本。

4.最后

在使用 fetchpriority  属性时,需要注意一下方面:

1)fetchpriority 并不能确保优先级较高的资源会在同类型的其他(优先级较低)资源之前加载。 

2)fetchpriority 本身不应用于控制加载顺序。 

3)fetchpriority 并非强制性指令,它不能强制浏览器获取资源或阻止其获取,是否获取资源取决于浏览器。

总之,fetchpriority 属性也应该被谨慎使用,过度使用可能会导致网页速度变慢,适得其反。

大家都在看

继续滑动看下一个

WEB性能:利用Priority Hints和fetchpriority提升资源加载速度

小懒 FED实验室
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存