Web浏览器图片延迟(惰性)加载

1,411 阅读12分钟

现如今网络上大部分的浏览器都支持延迟加载图像,这个视频 demo 对这个特性进行了演示。

在 Chrome 76 及更高版本中,您可以使用loading属性延迟加载图像,而无需编写自定义延迟加载代码或使用单独的 JavaScript 库。让我们深入了解细节。

浏览器兼容性

大多数基于 Chromium 的流行浏览器(Chrome、Edge、Opera)和Firefox 都支持<img loading=lazy> 。 WebKit (Safari)针对这个特性的实现也 正在进行中.caniuse.com 在有关于跨浏览器支持上也有详细信息。不支持loading属性的浏览器会直接忽略它而不会产生负面影响。

为什么是浏览器级别的延迟加载?

根据 HTTPArchive, 图像是大多数网站请求最多的资源类型,通常比其他资源占用更多的网络带宽。 At the 90th percentile, 在桌面和移动设备上,网站发送大约4.7MB的图片。 这将是很多 猫的图片.

目前,有两种方法可以延迟加载离屏图像:

这两种选择都可以让开发人员设置延迟加载功能,并且许多开发人员已经构建了第三方库,提供一些更易于使用的抽象概念。但是,由于浏览器直接支持延迟加载,因此不需要外部库。浏览器级延迟加载还确保即使在客户端禁用 JavaScript,延迟加载图像仍然有效。

loading 属性

今天,Chrome已经根据图像相对于设备视口的位置以不同的优先级加载图像。视图下方的图像会以较低的优先级加载,但它们仍然会尽快获取。

在 Chrome 76+ 中,您可以使用loading属性来延迟那些屏幕外图像的加载,它们可以能通过滚动到达视口:

<img src="image.png" loading="lazy" alt="…" width="200" height="200">

以下是loading属性支持的值:

  • auto: 浏览器的默认延迟加载行为,与不包含该属性相同。
  • lazy:推迟加载资源,直到它达到与视口的计算距离(离视口多大距离开始加载)。
  • eager:立即加载资源,无论它位于页面上的什么位置。

注意:尽管在 Chromium 中可用auto,但规范中未提及该值。由于它可能会发生变化,我们建议在它被支持之前不要使用它。

距离视口阈值

在折叠上方的所有图像(即无需滚动即可立即查看)正常加载。那些远低于设备视口的内容只有在用户滚动到它们(折叠、阈值)附近时才会被获取。

Chromium 的延迟加载的实现是试图确保屏幕外图像足够早地加载,以便在用户滚动到它们附近时它们已经完成加载。在它们到达视口可见区域之前获取附近的图像,我们最大限度地增加它们在到达可见区域时已经加载完成的机会。

与 JavaScript 延迟加载库相比,获取滚动到视图中的图像的阈值可能被认为是保守的。Chromium 正在考虑更好地将这些阈值与开发人员的期望保持一致。


在 Android 上使用 Chrome 进行的实验表明,在 4G 网络情况下,97.5% 的延迟加载,折叠下方图像在可见后 10 毫秒内完全加载。即使在较慢的 2G 网络情况下,92.6% 的折叠下方图像在 10 毫秒内完全加载。这意味着浏览器级别的延迟加载对滚动到视图中的元素可见性提供了稳定的体验。

距离阈值并不是固定的,取决于几个因素:

您可以在Chromium source找到不同有效连接类型的默认值。随着 Chrome 团队改进启发式方法来确保何时开始加载,这些数字,甚至是仅在达到与视口一定距离时才获取的方法,在不久的将来可能会发生变化。


在 Chrome 77+ 中,您可以通过在 DevTools 中限制网络来试验这些不同的阈值。同时,您需要使用该about://flags/#force-effective-connection-type标志覆盖浏览器的有效连接类型。

流量节省 和 视口距离阈值(Improved data-savings and distance-from-viewport thresholds)

截至 2020 年 7 月,Chrome 已进行了重大改进,以对齐图像延迟加载与视口的距离阈值,以更好地满足开发人员的期望。

在快速网络(例如 4G)上,我们将 Chrome 的视口距离阈值从 降低3000px1250px和在较慢的网络(例如 3G)上,将阈值从 更改4000px2500px。这种变化实现了两件事:

  • <img loading=lazy> 行为更接近 JavaScript 延迟加载库提供的体验。
  • 新的视口距离阈值仍然允许我们能够保证图像在用户滚动到它们时尽可能已完成加载。

您可以在下面的快速网络 (4G) 上,看到新旧视口距离阀值之间的比较:

旧阀值 VS 新阀值:

image.png

以及新的阈值与 LazySizes(一个流行的 JS 延迟加载库):

image.png


为确保使用最新版本的 Chrome 用户也能从新阈值中受益,我们向后移植(打补丁)了这些更改,以便 Chrome 79 - 85(包括)也能使用它们。如果尝试比较旧版 Chrome 与新版 Chrome 的流量节省情况,请记住这一点。

我们致力于与 Web 标准社区合作,探索如何在不同浏览器之间实现视口距离阈值的更好一致性。

图像应包含尺寸属性

当浏览器加载图像时,它不会立即知道图像的尺寸,除非这些尺寸被明确指定。为了让浏览器能够在页面上为图像保留足够的空间,建议所有<img>标签都包含widthheight属性。如果没有指定尺寸,可能会发生布局偏移,这在需要一些时间加载的页面上(网络差、页面内容多)更为明显。

<img src="image.png" loading="lazy" alt="…" width="200" height="200">

又或者,直接以内联样式指定它们的值:

<img src="image.png" loading="lazy" alt="…" style="height:200px; width:200px;">

设置尺寸的最佳实践适用于<img>标签,无论它们是否被延迟加载。使用延迟加载,这会它们之间的关系变得更加贴近。在现代浏览器中设置widthheight显示图像还允许浏览器推断它们的内在大小。

在大多数情况下,如果不包含尺寸,图像仍然是延迟加载的,但有一些边缘情况您应该注意。如果没有widthheight指定,图像尺寸首先会是0×0的像素。如果您有这样的图像库(瀑布流的意思吧),浏览器可能会得出结论,所有这些图像一开始都处于视口之内,因此每个图像几乎不占用空间,也没有图像被推到屏幕外。在这种情况下,浏览器确定所有这些对用户都是可见的,并决定加载所有内容。

此外,指定图像尺寸会降低发生布局变化的机率。如果您无法为您的图像设置尺寸,延迟加载 可能是 节省网络资源 和 潜在的布局偏移风险 之间 的权衡点。

虽然 Chromium 中的延迟加载是以图像一旦可见就很可能被加载的这样一种方式来实现的,但它们仍有可能小概率未被加载。在这种情况下,在此类缺失widthheight属性的图像上则会增加它们对累积布局偏移的影响。


看看这个demo,看看loading属性是如何处理 100 张图片的。

使用<picture>元素定义的图像也可以延迟加载:

<picture>
  <source media="(min-width: 800px)" srcset="large.jpg 1x, larger.jpg 2x">
  <img src="photo.jpg" loading="lazy">
</picture>

浏览器会识别任何一个<source>元素并决定加载哪个图像,loading属性只需要包含在备用的<img>元素中。

避免延迟加载可见视口(首屏)中的图像

您应该避免为首屏中的任何图像设置loading=lazy。 如果可能的话,建议仅添加loading=lazy到位于折叠下方的图像。立即加载的图像可以马上获取,而延迟加载的图像则需要等待浏览器知道图像在页面上的位置,这依赖于 IntersectionObserver 。


In Chromium, the impact of images in the initial viewport being marked with loading=lazy on Largest Contentful Paint is fairly small, with a regression of <1% at the 75th and 99th percentiles compared to eagerly loaded images. 在 Chromium 中,在最大内容绘制上用loading=lazy标记的初始视图中(首屏)影响是相当小的,与立即加载的图像相比,在第75和99个百分点的回归值小于1%。

通常,视口内的任何图像都应该使用浏览器的默认值立即地加载。对于视口内图像的这种情况,您无需为此指定loading=eager

<!-- 视口可见区域 -->
<img src="product-1.jpg" alt="..." width="200" height="200">
<img src="product-2.jpg" alt="..." width="200" height="200">
<img src="product-3.jpg" alt="..." width="200" height="200">

<!-- 屏幕外图像 -->
<img src="product-4.jpg" loading="lazy" alt="..." width="200" height="200">
<img src="product-5.jpg" loading="lazy" alt="..." width="200" height="200">
<img src="product-6.jpg" loading="lazy" alt="..." width="200" height="200">

优雅降级

尚不支持该loading属性的浏览器将忽略其存在。虽然这些浏览器并不会得到延迟加载的好处,但为其设置属性对它们并没有负面影响。

常见问题

是否有计划在 Chrome 中自动延迟加载图像?#

如果在 Chrome for Android 上启用了Lite 模式,Chromium 已经自动延迟加载任何非常适合延迟的图像。这主要针对那些关注流量节省的用户。

我可以更改图片被触发加载之前所需要的最近距离吗?#

这些值是硬编码的,不能通过 API 更改。但是,随着浏览器对不同的阈值距离和变量进行试验,它们将来可能会发生变化。

CSS 背景图片可以利用该loading属性吗?#

不能,它目前只能与<img>标签一起使用。

延迟加载设备视口内的图像是否有缺点?#

避免将loading=lazy放置在首屏图像上(折叠图像上方)是更安全的做法,因为 Chrome 不会在预加载扫描程序中预加载loading=lazy图像的。

loading属性如何区分在视口中且不是立即可见的图像(例如:在轮播后面,或被CSS隐藏在特定的屏幕大小)?#

只有在设备视口下方根据计算距离加载的图像才会延迟加载。视口上方的所有图像,无论它们是否立即可见,都会正常加载。

如果我已经在使用第三方库或脚本来延迟加载图像怎么办?#

The loading attribute should not affect code that currently lazy-loads your assets in any way,但有一些重要的几个点需要考虑:

  1. 如果您的自定义延迟加载器尝试比 Chrome 正常加载图像或帧更快地加载它们(即,距离大于视口距离阈值——它们仍会基于正常浏览器行为去延迟和加载。
  2. 如果您的自定义延迟加载器使用比浏览器更短的距离来确定何时加载特定图像,那么该行为将符合您的自定义设置。

第三方库和loading="lazy"一起使用的一个重要原因之一就是为还不支持该属性的浏览器提供 polyfill。

如何处理还不支持延迟加载的浏览器?#

创建一个 polyfill 或使用第三方库在您的网站上延迟加载图像。loading属性可用于检测浏览器是否支持该功能:

if ('loading' in HTMLImageElement.prototype) {
  // supported in browser
} else {
  // 加载 polyfill/third-party 库
}

例如,lazysizes是一个流行的 JavaScript 延迟加载库。您能够检测对属性的支持情况,当在loading不被支持时,用lazysizes作为备用库使用。其工作原理如下:

  • 替换<img src><img data-src>以避免在不受支持的浏览器中立即加载。如果loading属性支持,交换data-srcsrc
  • 如果loading不支持,请加载备用库(lazysizes)并启动它。根据lazysizes 文档,您可以使用lazyload类名来告诉(提示)lazysizes 要延迟加载哪些图像。
<!-- 正常加载视口内的图像 -->
<img src="hero.jpg" alt="…">

<!-- 延迟加载其余图像 -->
<img data-src="unicorn.jpg" alt="…" loading="lazy" class="lazyload">
<img data-src="cats.jpg" alt="…" loading="lazy" class="lazyload">
<img data-src="dogs.jpg" alt="…" loading="lazy" class="lazyload">

<script>
  if ('loading' in HTMLImageElement.prototype) {
    const images = document.querySelectorAll('img[loading="lazy"]');
    images.forEach(img => {
      img.src = img.dataset.src;
    });
  } else {
    // 动态导入LazySizes库
    const script = document.createElement('script');
    script.src =
      'https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.1.2/lazysizes.min.js';
    document.body.appendChild(script);
  }
</script>

这是此模式的demo。在 Firefox 或 Safari 等浏览器中尝试一下,看看回退的效果。


lazysizes 库还提供了一个加载插件,该插件在可用时使用浏览器级延迟加载,但在需要时回退到库的自定义功能。

Chrome 是否也支持 iframe 的延迟加载?#

<iframe loading=lazy>最近标准化,并且已经在 Chromium 中实现。这允许您使用该loading属性延迟加载 iframe 。一篇关于 iframe 延迟加载的专门文章将很快在 web.dev 上发布。

loading属性对 iframe 的影响与对图像的影响不同,具体取决于 iframe 是否隐藏。(隐藏的 iframe 通常用于分析或通信目的。)Chrome 使用以下标准来确定 iframe 是否隐藏:

  • iframe 的宽度和高度为 4 px 甚至更小。
  • 应用 display: nonevisibility: hidden
  • iframe 使用负 X 或 Y 定位放置在屏幕外。

如果 iframe 满足这些条件中的任何一个,Chrome 会将其视为隐藏的,并且在大多数情况下不会延迟加载它。未被隐藏的iframe只会在它们位于距离视口阈值范围内时加载。占位符显示仍在获取的延迟加载的 iframe。(A placeholder shows for lazy-loaded iframes that are still being fetched)

浏览器级延迟加载如何影响网页上的广告?#

所有以图像或 iframe 的形式向用户展示的广告,就像任何其他图像或 iframe 一样延迟加载。

打印网页时如何处理图像?#

尽管该功能目前不在 Chrome 中,但有一个open issue可以确保在打印页面时立即加载所有图像和 iframe。

Lighthouse 是否识别浏览器级别的延迟加载?#

早期版本的 Lighthouse 仍然会强调那些图像上使用loading=lazy的页面,需要加载屏幕外图像的策略。Lighthouse 6.0及更高版本更好地考虑了可能使用不同阈值的离屏图像延迟加载方法,允许它们通过Defer offscreen images audit。

结论#

Baking in support for lazy-loading images can make it significantly easier for you to improve the performance of your web pages. 烘焙??? 对延迟加载图像的支持可以让您更轻松地提高网页的性能。

在 Chrome 中启用此功能后,您是否注意到任何异常行为?提交错误

翻译自:web.dev/browser-lev…