什么是图像延迟加载?

延迟加载图像是 Web 和应用程序开发中的一组技术,可将页面上的图像加载延迟到稍后的时间点 - 当实际需要这些图像时,而不是预先加载它们。这些技术有助于提高性能、更好地利用设备资源并降低相关成本。

英语中的“懒惰”一词通常被归因于尽可能长时间避免工作的行为。

同样,延迟加载将页面上的资源加载延迟到实际需要时。与通常情况下在页面加载后立即加载这些资源不同,这些资源的加载被推迟到用户实际需要查看它们的那一刻。

延迟加载技术几乎可以应用于页面上的所有资源。例如,在单页应用程序中,如果一个 JS 文件直到以后才需要,最好不要一开始就加载它。如果预先不需要图像,则在实际需要查看时稍后加载。

为什么要延迟加载图像?

延迟加载延迟加载页面上不需要的图像。图像在页面加载时对用户不可见,稍后在用户滚动并且图像实际变为可见时加载。如果用户从不滚动,则不会加载用户不可见的图像。

它具有两个主要优点。

1. 性能提升

作为网站管理员,这对您来说是最重要的——更好的性能和加载时间。

通过延迟加载,您可以减少最初需要在页面上加载的图像数量。更少的资源请求意味着更少的字节下载和更少的对用户可用的有限网络带宽的竞争。这确保了设备能够更快地下载和处理剩余资源。因此,与没有延迟加载的页面相比,页面可以更快地使用。

2. 降低成本

您的第二个好处是在交付成本方面。图像交付或任何其他资产的交付通常根据传输的字节数收费。

如前所述,通过延迟加载,如果图像不可见,则永远不会加载。因此,您可以减少页面上传递的总字节数,尤其是对于从页面反弹或仅与页面顶部交互的用户。从您的交付网络传输的字节数减少会降低交付成本。随着我们进一步探索延迟加载,这将变得更加明显。

哪些图像可以延迟加载?

延迟加载的基本思想很简单——推迟加载现在不需要的任何内容。对于图像,它通常转换为用户不可见的任何图像,可以延迟加载。

当用户向下滚动页面时,图像占位符开始进入视口(网页的可见部分)。当它们变得可见时,我们会触发这些图像的加载。

您可以使用Google Lighthouse 审计工具找出哪些图像适合延迟加载,以及在初始页面加载时可以节省多少字节。此工具执行的审核有一个专门用于屏幕外图像的部分。

延迟加载不仅对于良好的性能至关重要,而且对于提供良好的用户体验也是至关重要的。

图像的延迟加载技术

可以通过两种方式加载网页上的图像 - 使用 标签,或使用 CSS background 属性。让我们先看看两者中比较常见的 标签,然后再看看 CSS 背景图片。

标签中延迟加载图片的一般概念

延迟加载图像可以分为两个步骤:

第一步是防止预先加载图像。对于使用<img>标签加载的图片,浏览器使用标签的src属性来触发图片加载。无论它是 HTML 中的第 1 个还是第 1000 个图像并且在屏幕外,如果浏览器获取该src属性,它将触发图像加载。

因此,要延迟加载此类图像,请将图像 URL 放在除src. 假设我们在data-src图像标签的属性中指定了图像 URL。现在它src是空的,浏览器不会触发图像加载

1
<img data-src="https://ik.imagekit.io/demo/default-image.jpg" />

现在我们已经停止了前期加载,我们需要告诉浏览器何时加载图像。

为此,我们检查一旦图像(即它的占位符)进入视口,我们就会触发加载。

要检查图像何时进入视口,有两种方法:

使用 Javascript 事件触发图像加载

在这种技术中,我们在浏览器中使用事件监听scrollresize,orientationChange事件。当用户滚动页面时,滚动事件是一个显而易见的检查事件。resize 和orientationChange 事件对于延迟加载同样重要。当浏览器窗口的大小改变时,会发生 resize 事件。当设备从横向模式旋转到纵向模式时会触发orientationChange 事件,反之亦然。在这种情况下,屏幕上可见的图像数量会发生变化。因此,我们需要触发这些图像的加载。

当这些事件中的任何一个发生时,我们会发现页面上所有要延迟加载且尚未加载的图像。从这些图像中,我们检查哪些图像现在在视口中。这是使用图像的顶部偏移量、当前文档滚动顶部和窗口高度来完成的。如果它已进入视口,我们从data-src属性中选择 URL 并将其放入src属性中。这会触发图像加载。我们还删除了lazy标识要为稍后触发的事件延迟加载的图像的类。加载所有图像后,我们将删除事件侦听器。

当我们滚动时,滚动事件会快速触发多次。因此,为了性能,我们添加了一个小的超时来限制延迟加载函数的执行。

这是这种方法的一个工作示例。

如果您注意到,示例中的前 3 个图像是预先加载的。URL 直接存在于src属性中,而不是data-src属性中。这对于良好的用户体验至关重要。由于这些图像位于页面顶部,因此应尽快使它们可见。我们不能等待事件或 JS 执行来加载它们。

使用 Intersection Observer API 触发图像加载

Intersection Observer API 是浏览器中相对较新的 API。它使得检测元素何时进入视口并在它进入时采取行动变得非常简单。在之前的方法中,我们必须绑定事件,牢记性能,并实现一种计算元素是否在视口中的方法。Intersection Observer API 使这变得非常简单,有助于避免数学运算,并提供出色的性能。

使用 Intersection Observer API 延迟加载图像的示例:

我们将观察者附加到所有要延迟加载的图像上。一旦 API 检测到元素已进入视口,使用isIntersecting属性,我们从属性中选择 URLdata-src并将其移动到src属性中以供浏览器触发图像加载。完成此操作后,我们从图像中删除惰性类,并从该图像中删除观察者。

如果您比较两种方法(事件侦听器与 Intersection Observer)加载图像所花费的时间,您会发现使用 Intersection Observer API,图像加载被触发得更快,而且网站在滚动时不会显得迟缓. 在涉及事件侦听器的方法中,我们必须添加超时以使其具有性能,这对用户体验的影响很小,因为图像加载被触发时会有轻微的延迟。

但是,并非所有浏览器都支持 Intersection Observer API 。因此,我们需要在不支持 Intersection Observer API 的浏览器中回退到事件侦听器方法。我们在上面的例子中已经考虑到了这一点。

本机延迟加载

在他们最近的更新中,谷歌在 Chrome 浏览器的最新版本 - Chrome 76 中增加了对原生延迟加载的支持。所有基于 Chromium 的浏览器,即 Chrome、Edge 和 Safari 以及 Firefox。您可以在caniuse.com上找到有关浏览器支持本机延迟加载的更多详细信息。

随着浏览器端的支持开始发挥作用,现在,开发者只需要在嵌入图像时添加一个“加载”属性,就可以在他们的网站上实现延迟加载。

事实上,一个人甚至不需要成为一名开发人员来完成这项工作。HTML 的一些基本知识足以实现“加载”属性,使更多网站管理员可以访问此功能。

所以代码现在看起来像 -

1
2
<img src="example.jpg" loading="lazy" alt="..." />
<iframe src="example.html" loading="lazy"></iframe>

loading 属性支持以下值:

  • **lazy **推迟资产的加载,直到它与视口达到一定距离。
  • eager页面加载后立即加载资产,无论它们放置在页面上的哪个位置,无论是在页面折叠的上方还是下方。
  • auto此值触发默认延迟加载。基本上,它与不包括加载属性相同。

但是,对于不支持本机延迟加载的浏览器,需要应用上述实现它的技术。

为防止在下载延迟加载的图像时重排周围的内容,请确保将heightwidth属性添加到<img>元素或直接以内联样式指定它们的值:

1
2
<img src="image1.jpg" loading="lazy" alt="…" width="300" height="300">
<img src="image2.jpg" loading="lazy" alt="…" style="height:300px; width:300px;">

延迟加载 CSS 背景图像

<img />标签之后,背景图片是在网页上加载图片的最常见方式。对于<img />标签,浏览器有一个非常简单的方法——如果图片 URL 可用,让我们加载图片。

使用 CSS 背景图像并不是那么简单。为了加载 CSS 背景图像,浏览器需要构建 DOM(文档对象模型)树以及 CSSOM(CSS 对象模型)树,以决定 CSS 样式是否适用于当前文档中的 DOM 节点。

如果指定背景图像的 CSS 规则不适用于文档中的元素,则浏览器不会加载背景图像。如果 CSS 规则适用于当前文档中的元素,则浏览器会加载图像。

起初这可能看起来很复杂,但同样的行为构成了延迟加载背景图像技术的基础。简单来说,我们欺骗浏览器在元素进入视口之前不将背景图像 CSS 属性应用到元素。

这是一个延迟加载 CSS 背景图像的工作示例。

这里要注意的一件事是延迟加载的 Javascript 代码仍然是相同的。我们正在使用 Intersection Observer API 方法并回退到事件侦听器。诀窍在于CSS。

ID 为 bg-image 的元素background-image在 CSS 中指定了一个。但是,当类lazy被添加到这个元素时,在 CSS 中我们会覆盖该background-image属性并将其设置为无。

由于规则,#bg-image.lazyclass 结合在 CSS 中比 just 具有更高的偏好,浏览器最初#bg-image将属性应用于元素。background-image: none当我们向下滚动时,Intersection Observer(或事件侦听器)检测到图像在视口中并删除 class lazy。这会更改适用的 CSS 并将实际background-image属性应用于触发背景图像加载的元素。

延迟加载图像带来更好的用户体验

延迟加载带来了巨大的性能优势。对于在页面上加载数百个产品图像的电子商务公司,延迟加载可以显着改善初始页面加载时间,同时减少带宽消耗。

然而,很多公司并没有选择延迟加载,因为他们认为这不利于提供出色的用户体验,理由是“初始占位符很丑”、“加载时间很慢”等。

我们如何通过延迟加载图像来解决有关用户体验的这些问题?

1.使用正确的图像占位符

占位符是在加载实际图像之前出现在容器中的内容。通常,我们看到开发人员使用纯色占位符来存储图像,或者使用单个图像作为所有图像的占位符。

我们在示例代码中也使用了相同的方法。我们所有的图像背景都使用纯浅灰色。但是,我们可以做得更好,以提供更令人愉悦的用户体验。

看看我们的图像更好的占位符的一些例子:

a) 主色占位符

我们不是使用固定颜色作为图像占位符,而是从原始图像中找到主要颜色并将其用作占位符。

这种技术已经在谷歌图片搜索结果和 Pinterest 中使用了相当长的一段时间。

这可能看起来很复杂,但实现这一点的一个非常简单的方法是首先将图像缩小到 1x1 像素,然后将其放大到占位符的大小 - 一个非常粗略的近似值,但很简单,没有大惊小怪获得单一主色的方法。

b) 低质量图像占位符 (LQIP)

我们可以进一步扩展上述使用主色占位符的想法。

我们没有使用单一颜色,而是使用原始图像的质量非常低、模糊的版本作为占位符。它不仅看起来更好,还让用户对实际图像中的预期有所了解,同时给人一种图像加载正在进行的感觉。这对于改善感知加载体验非常有用。

Facebook 和 Medium.com 等公司已将这种技术用于其网站和应用程序上的图像。

使用 ImageKit 的 LQIP 图像 URL 示例

1
2
3
4
5
<!-- Original image at 400x300 -->
<img src="https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300" alt="original image" />

<!-- Low quality image placeholder with same dimensions -->
<img src="https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300,bl-30,q-50" alt="dominant color placeholder" />

LQIP 大小为 1300 字节,比原始图像小近 10 倍,在视觉体验方面比任何其他占位符技术都有显着改进。

2.为图片加载增加一些缓冲时间

当我们在上面讨论触发图像加载的不同方法时,我们检查了图像进入视口的时间点,即图像占位符的上边缘与视口的下边缘重合的时间点。

问题
通常,用户快速滚动页面,图像需要一些时间才能加载并出现在屏幕上。在这种情况下,再加上加载图像事件可能会因节流而延迟触发,您经常会遇到占位符进入视口的情况,用户在图像加载时等待几毫秒。这种延迟会导致糟糕的用户体验。

虽然使用 Intersection Observers 加载图像或使用低质量的图像占位符可提供更好的加载性能和用户体验,但您可以使用另一个简单的技巧来确保图像在进入视口时始终完全加载 - 引入边距到图像的触发点。

解决方案
不是在图像完全进入视口时才加载图像,而是在图像距离进入视口 500 像素时加载图像。这在加载触发器和视口中的实际条目之间提供了额外的时间来加载图像。

使用 Intersection Observer API,您可以使用 root 参数和 rootMargin 参数(作为标准 CSS 边距规则)来增加被认为找到“交叉点”的有效边界框。

使用事件监听器方法,我们可以使用一个正数来添加一些阈值,而不是检查图像边缘和视口边缘之间的差异是否为 0。

如果您还没有注意到,在我们所有的示例中,第三张图像 (image3.jpg) 总是在前面加载,即使它在视口之外。这也是按照相同的原则完成的 - 稍微提前加载而不是精确加载阈值以获得更好的用户体验。

如果您使用的是原生图像延迟加载方法,浏览器会自动计算与视口阈值的距离,以确定浏览器何时应该触发图像加载。浏览器会考虑浏览器中的图像类型、网络速度和数据保护程序设置来确定此阈值,同时牢记开发人员的期望和用户体验。

3.通过延迟加载避免内容转移

这是另一个微不足道的问题,如果解决,可以帮助保持良好的用户体验。

问题
当没有图像时,浏览器不知道要在封闭容器中显示的内容的尺寸。如果我们不使用 CSS 指定它,封闭容器将没有尺寸,即 0 x 0 像素。因此,当图像被加载时,浏览器会调整封闭容器的大小以适应图像。
这种布局的突然变化导致其他元素移动,这称为内容移动。正如 Smashing Magazine 的这篇内容转换文章和视频中所展示的,当图像加载时内容突然移动,这对用户来说是一种相当不愉快的体验。

解决方案
这可以通过为封闭容器指定高度和/或宽度来避免,以便浏览器可以使用已知高度和宽度绘制图像容器。稍后,当图像加载时,由于已经指定了容器大小并且图像完全适合该大小,因此容器周围的其余内容保持不变。

4.不要懒加载所有图片

这是开发人员经常犯的另一个错误——延迟加载页面上的所有图像。这可能会减少初始页面加载,但也会导致糟糕的用户体验,因为很多图像,即使是网页顶部的图像,在 Javascript 执行之前都不会显示。

以下是确定哪些图像应该延迟加载的一些一般原则。

a)任何出现在视口中或网页开头的图像都不应延迟加载。这适用于任何标题图像、营销横幅、徽标等,因为用户应该在页面加载后立即看到它们。

此外,由于移动设备和桌面设备具有不同的屏幕尺寸,它们最初会在屏幕上显示不同数量的图像。因此,您需要考虑设备类型来决定预先加载哪些资源以及延迟加载哪些资源。

**b)**任何稍微偏离视口的图像都不应延迟加载。这是基于前面讨论的点 - 稍微提前加载。因此,比方说,任何 500 像素的图像或从视口底部开始的单个滚动也可以预先加载。

**c)**如果页面不太长,可能只有一个或两个滚动条,或者视口外的图像少于 5 张,则可以完全避免延迟加载。

就性能而言,它不会为最终用户提供任何显着的好处。您在页面上加载以启用延迟加载的附加 JS 将抵消延迟加载如此少量图像所带来的任何好处。

延迟加载的 Javascript 依赖

延迟加载的整个想法取决于用户浏览器中 Javascript 执行功能的可用性。尽管原生延迟加载承诺会消除这种依赖,但浏览器支持率仍接近 70%,但如果您要在所有浏览器中提供相同的体验,您仍然需要使用 JS 库。

用于在您的网站上延迟加载的流行 Javascript 库

由于浏览器环境和实现细节可能因浏览器和设备而异,因此最好使用经过验证的库来进行延迟加载。

这是一个流行的库和特定于平台的插件的列表,它们可以让您以最小的努力实现延迟加载

yall.js

  • 使用 Intersection Observer 并回退到基于事件的延迟加载。
  • 支持所有主要的 HTML 元素类型,但不支持背景图像。
  • 也适用于 IE11+。

lazysizes

  • 非常流行和广泛的功能。
  • 也支持响应式图像 srcset 和 sizes 属性。
  • 即使没有 Intersection Observer 也能实现高性能。

jQuery Lazy

  • 一个简单的、基于 jquery 的延迟加载库。

WeltPixel Lazy Loading Enhanced

  • 用于延迟加载图像的 Magento 2 扩展。

Magento Lazy Image Loader

  • 用于延迟加载图像的 Magento 1.x 扩展。

Shopify Lazy Image Plugin

  • 用于延迟加载图像的 Shopify 扩展。
  • 虽然是付费的。

Wordpress A3 Lazy Load

  • Wordpress 的图像延迟加载插件。

如何测试延迟加载是否有效?

实施延迟加载后,您需要检查网站上图像的行为是否符合预期。最简单的方法是在 Chrome 浏览器中打开开发者工具。

转到网络选项卡 > 图像。

在这里,当您第一次刷新页面时,只有要加载的图像才会被加载。然后,当您开始向下滚动页面时,将触发并加载其他图像加载请求。

您还可以在此视图的瀑布列中注意到图像加载的时间。它将帮助您识别图像加载问题(如果有)或触发图像加载的问题。

另一种方法是在实施更改后在您的页面上运行 Google Chrome Lighthouse 审核报告,并在“屏幕外图像”部分下查找建议。

结论

延迟加载如果实施得当,将显着提高网页的加载性能,通过减少预先加载的不必要资源来减少页面大小和交付成本,同时保持页面上必要内容的完整性。

DEMO