图片懒加载完整指南

Avatar of Rahul Nanwani
Rahul Nanwani 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

图片至关重要。无论是营销横幅、产品图片还是 logo,都无法想象一个没有图片的网站。但遗憾的是,图片通常是体积较大的文件,使其成为页面臃肿的最大因素之一。根据 HTTP Archive 的 图片现状报告,桌面端网页的中位数大小为 1511 KB,其中图片占了近 45%(650 KB)。

也就是说,我们不能简单地去除图片。它们对于整体用户体验来说太重要了。相反,我们需要使我们的网页在加载图片时速度更快。在本指南中,我们将涵盖图片懒加载的所有来龙去脉,这是一种通过推迟图片加载直到需要时才加载的技术,有助于提高网页加载时间。

这篇文章很好地详细介绍了懒加载主题以及所有需要考虑的因素、工具、技术等。但它是在原生懒加载开始流行之前撰写的,因此在确定实施方案时,最好将原生懒加载纳入其中。

在深入探讨之前,这里有一个示例视频来演示这个概念。简而言之,页面上会渲染一个灰色的占位符框,直到它滚动到视野中——此时,实际图片会加载到框的位置。

第 1 章:什么是懒加载?

我们通常将“懒惰”一词与尽可能避免工作或完全不想做任何事情的行为联系起来。

类似地,懒加载会推迟页面上资源的加载,直到它们真正需要时才加载。我们不会像通常那样立即加载它们,而是允许它们稍后加载。

懒加载是一组在 Web 和应用程序开发中使用的技术,它会推迟页面上资源的加载,直到真正需要时才加载,而不是一开始就加载它们。这些技术有助于提高性能、更好地利用设备资源并降低相关成本。

懒加载技术几乎可以应用于页面上的任何资源。例如,如果最好不要一开始就加载,那么 JavaScript 文件也可以被延迟加载。图片也是如此——在需要时加载。

在本指南中,我们将重点关注图片懒加载,但了解它可以应用于其他资源也很重要。

第 2 章:为什么要使用懒加载?

如果用户从未滚动到包含图片的页面部分,那么用户将永远看不到该图片。它也不会一开始就加载,因为,嘿,它从未被需要过。

您可能已经开始看到这对您和用户有什么好处。以下是懒加载带来的两个优势。

性能提升

显而易见的好处是,我们可以获得加载速度更快的网页。懒加载减少了页面上需要预加载的图片数量。更少的图片请求意味着更少的字节需要下载。更少的字节需要下载意味着页面渲染速度比那些字节和请求正在进行时更快。

这确保了任何网络上的任何设备都能更快地下载和处理剩余的资源。因此,从请求到渲染的时间缩短了,页面也更早地可用。双赢!

成本降低

第二个好处是针对您作为网站管理员。云托管服务(如内容分发网络 (CDN)、Web 服务器或存储)会根据传输的字节数收取图片(或任何资源)的费用。如果用户从未访问到懒加载的图片,那么它可能永远不会被加载。因此,您可以减少页面上传输的总字节数,并最终节省一些费用。对于那些立即离开页面或只与内容顶部交互的用户来说,尤其如此。

从您的交付网络或服务器传输的字节数减少,从而降低了交付成本。当我们在接下来的部分中探索懒加载时,这一点将变得更加明显。

您能节省多少?您可以使用 Google Lighthouse 审核工具 找出哪些图片适合懒加载以及在初始页面加载时可以节省多少字节。它有一个专门用于 屏幕外图片 的部分。您还可以使用 ImageKit 的网站分析器 来识别您的网站是否使用了懒加载,以及页面上其他重要的与图片相关的优化。

懒加载不仅对良好的性能至关重要,而且对提供良好的用户体验也至关重要。由于将性能和用户体验与懒加载相结合既重要又具有挑战性,因此在了解了不同的图片懒加载方法之后,我们将在本指南的后续部分更详细地探讨此主题。

第 3 章:图片懒加载技术

我们有两种常见的图片加载方式:<img> 标签和 CSS 的 background-image 属性。我们首先来看两者中更常见的一种,即 <img> 标签,然后再转向 CSS 背景图片。

在图片标签中懒加载图片

让我们从图片的典型 HTML 标记开始

<img src="/path/to/some/image.jpg">

懒加载图片的标记非常相似。

第一步是阻止图片预加载。浏览器使用标签的 src 属性来触发图片加载。无论它是 HTML 中的第一个图片还是第 1000 个图片,这都没有关系。如果浏览器获取了 src 属性,它将触发图片下载,无论它是否在当前视野中。

要推迟加载,请将图片 URL 放入除 src 之外的其他属性中。假设我们在图片标签的 data-src 属性中指定图片 URL。现在 src 为空,浏览器不会触发图片加载。

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

现在我们阻止了图片加载,我们需要告诉浏览器何时加载它。否则,它将永远不会发生。为此,我们检查一旦图片(即其占位符)进入视口,我们就触发加载。

有两种方法可以检查图片何时进入视口。让我们通过工作代码示例来查看这两种方法。

方法 1:使用 JavaScript 事件触发图片加载

此技术在浏览器的 scrollresizeorientationChange 事件上使用事件监听器。scroll 事件非常直观,因为它会在滚动发生时监视用户在页面上的位置。resizeorientationChange 事件同样重要。resize 事件发生在浏览器窗口大小发生变化时,而 orientationChange 事件则在设备从横向旋转到纵向或反之亦然时触发。

我们可以使用这三个事件来识别屏幕的变化,并确定屏幕上变得可见的图像数量,并相应地触发它们的加载。

当任何这些事件发生时,我们会找到页面上所有被延迟加载的图像,并从这些图像中检查哪些图像当前在视口中。这是通过使用图像的顶部偏移量、当前文档顶部位置和窗口高度来完成的。如果图像进入了视口,我们会从data-src属性中获取 URL 并将其移动到src属性,从而加载图像。

请注意,我们会要求 JavaScript 选择包含lazy类的图像。一旦图像加载完成,我们会删除该类,因为它不再需要触发事件。并且,一旦所有图像都加载完成,我们也会删除事件监听器。

当我们滚动时,滚动事件会快速触发多次。因此,为了提高性能,我们在脚本中添加了一个小的超时,它会限制延迟加载函数的执行,以避免它阻塞浏览器中同一线程中运行的其他任务。

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

请注意,此示例中的前三个图像是在前端加载的。URL 直接位于src属性中,而不是data-src属性中。这对良好的用户体验至关重要。由于这些图像位于页面顶部,因此应尽快使其可见。无需等待 JavaScript 加载它们。

方法 2:使用 Intersection Observer API 触发图像加载

Intersection Observer API 比较新。它可以轻松检测元素何时进入视口,并在发生时采取操作。在之前的方法中,我们必须绑定事件,牢记性能,并实现一种方法来计算元素是否在视口中。Intersection Observer API 通过避免数学计算并提供出色的性能来消除所有这些开销。

下面是一个使用 API 延迟加载图像的示例。我们将观察者附加到所有要延迟加载的图像上。一旦 API 检测到元素已进入视口,使用isIntersecting属性,我们就会从data-src属性中获取 URL 并将其移动到src属性,以便浏览器触发图像加载。完成后,我们从图像中删除lazy类,并从该图像中删除观察者。

如果您比较这两种方法(事件监听器与 Intersection Observer)的图像加载时间,您会发现使用 Intersection Observer API 加载图像的速度快得多,并且操作触发的速度也更快,而且即使在滚动过程中,网站也不会显得迟缓。在涉及事件监听器的方法中,我们必须添加一个超时以提高性能,这会对用户体验产生轻微的负面影响,因为图像加载会稍有延迟地触发。

但是,与任何新功能一样,并非所有浏览器都支持 Intersection Observer API。

此浏览器支持数据来自Caniuse,其中包含更多详细信息。数字表示浏览器在该版本及更高版本中支持该功能。

桌面

ChromeFirefoxIEEdgeSafari
5855不支持1612.1

移动/平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712712.2-12.5

因此,我们需要在不支持 Intersection Observer API 的浏览器中回退到事件监听器方法。我们在上面的示例中考虑了这一点。

第 4 章:延迟加载 CSS 背景图像

CSS 中常见的背景图像

.my-class {
  background-image: url('/path/to/some/image.jpg');
  /* more styles */
}

CSS 背景图像不像图像标签那样简单。要加载它们,浏览器需要构建DOM树和CSSOM树来确定 CSS 样式是否应用于当前文档中的 DOM 节点。如果指定背景图像的 CSS 规则不适用于文档中的元素,则浏览器不会加载背景图像。如果 CSS 规则适用于当前文档中的元素,则浏览器会加载图像。

嗯?这乍一看可能很复杂,但同样的行为构成了延迟加载背景图像技术的基石。简而言之,我们欺骗浏览器不将background-image CSS 属性应用于元素,直到该元素进入视口。

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

这里需要注意的一点是,延迟加载的 JavaScript 代码仍然相同。我们仍在使用 Intersection Observer API 方法,并回退到事件监听器。 “技巧”在于 CSS。

我们有一个 ID 为bg-image的元素,它具有background-image。但是,当我们将lazy类添加到元素时,我们可以通过在 CSS 中将其值设置为none来覆盖background-image属性。

由于具有 ID 和类的元素在 CSS 中比单独的 ID 具有更高的特异性,因此浏览器最初会将属性background-image: none应用于元素。当我们向下滚动时,Intersection Observer API(或事件监听器,具体取决于您选择哪种方法)检测到图像在视口中,它会从元素中删除lazy类。这会更改适用的 CSS 并将实际的background-image属性应用于元素,从而触发背景图像的加载。

第 5 章:通过延迟加载创建更好的用户体验

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

但是,许多公司不选择延迟加载,因为他们认为它违背了提供出色用户体验(即初始占位符很丑,加载时间很慢等)。

在本节中,我们将尝试解决围绕图像延迟加载的用户体验的一些问题。

技巧 1. 使用正确的占位符

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

到目前为止,我们查看的示例都采用了类似的方法:一个带有浅灰色背景的框。但是,我们可以做得更好,以提供更愉悦的用户体验。以下是在图像中使用更好占位符的两个示例。

主色占位符

我们不是为图像占位符使用固定颜色,而是从原始图像中找到主色并将其用作占位符。Google 在其图像搜索结果中以及 Pinterest 在其网格设计中使用了很长一段时间这种技术。

Example of Lazy Loading Images. Grid of images loads in with the primary color of the image behind them as a placeholder.
Pinterest 使用图像的主色作为图像占位符的背景色。(来源

这看起来实现起来可能很复杂,但是Manuel Wieser 有一个优雅的解决方案,可以通过将图像缩小到 1×1 像素,然后将其放大到占位符的大小来实现这一点——这是一个非常粗略的近似值,但它是一种简单易用的方法来获取单个主色。使用ImageKit,可以使用 ImageKit 中的链式转换获得主色占位符,如下所示。

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

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

占位符图像的大小仅为 661 字节,而原始图像的大小为 12700 字节——小了 19 倍。它提供了从占位符到实际图像的更流畅的过渡体验。

这是一个演示此效果对用户如何工作的视频。

低质量图像占位符 (LQIP)

我们可以进一步扩展使用主色占位符的想法。我们不是使用单一颜色,而是使用原始图像的非常低质量、模糊的版本作为占位符。它不仅看起来不错,而且还能让用户了解实际图像的样子以及图像加载正在进行的感觉。这对改善感知加载体验非常有用。Facebook 和 Medium 等公司已经利用了这种技术。

使用 ImageKit 的 LQIP 图像 URL 示例

<!-- 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 倍,并且在视觉体验方面比任何其他占位符技术都有了显著的改进。

这是一个演示此效果对用户如何工作的视频。


很明显,使用主色或 LQIP 占位符都可以提供从占位符到实际图像的更平滑的过渡,让用户了解占位符的位置将显示什么内容,并改善加载感知。

技巧 2:为图片加载预留缓冲时间

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

这样做的问题是,用户可能会快速滚动页面,而图片需要一些时间来加载并显示在屏幕上。再加上节流可能进一步延迟加载,用户最终可能会等待几毫秒才能看到图片。这对用户体验来说不是很好!

虽然我们可以使用 Intersection Observer API 来提高性能,并使用 LQIP 来实现更平滑的过渡,从而获得良好的用户体验,但还有一个简单的技巧可以确保图片在进入视口时始终完全加载:为图片的触发点引入一个边距。

与其在图片正好进入视口时加载它,不如在它进入视口之前,比如说 500px 的时候加载它。这会在加载触发器和图片实际进入视口之间提供额外的时间,让图片有时间加载。

使用 Intersection Observer API,您可以使用 root 参数以及 rootMargin 参数(作用类似标准 CSS 边距规则)来增加用于查找交叉点的有效边界框。使用事件监听器方法,我们可以使用正数添加一些阈值,而不是检查图片边缘和视口边缘之间的差值为 0。

如果您仔细观看下面的屏幕录制,您会注意到序列中的第五张图片在第三张图片进入视野时加载。类似地,第六张图片在第四张图片进入视野时加载,以此类推。这样,我们就能为图片完全加载提供足够的时间,并且在大多数情况下,用户根本不会看到占位符。

如果您之前没有注意到,在我们所有的示例中,第三张图片(image3.jpg)始终预先加载,即使它在视口之外。这也是遵循相同的原则:提前加载而不是在阈值处精确加载,以获得更好的用户体验。

技巧 3:避免内容重排

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

当没有图片时,浏览器不知道它将占据多大空间。如果我们不使用 CSS 指定它,那么包含容器将没有尺寸,即它将被读取为 0x0 像素。

当图片加载时,浏览器会将其放置到屏幕上并重新排列内容以适应它。布局的这种突然变化会导致其他元素四处移动,这被称为内容重排或偏移。Michael Scharnagl 深入解释了这如何造成不愉快的用户体验。

可以通过为包含容器指定高度和/或宽度来避免这种情况,以便浏览器可以使用已知的高度和宽度绘制图片容器。稍后,当图片加载时,由于容器大小已经指定并且图片完美地适合它,因此容器周围的其他内容不会移动。

技巧 4:避免对所有图片都使用懒加载

这是一个开发者经常犯的错误,因为总是认为延迟加载图片始终是好的,这非常诱人。但是,就像生活本身一样,好事也可能适得其反。懒加载可能会减少初始页面加载时间,但如果一些图片在不应该延迟加载时被延迟加载,也可能会导致糟糕的用户体验。

我们可以遵循一些通用原则来确定哪些图片应该懒加载。例如,任何存在于视口或网页开头的图片可能都不应该懒加载。这适用于任何标题图片、营销横幅、徽标或用户在最初访问页面时会看到的任何内容。此外,请记住,移动设备和桌面设备的屏幕尺寸不同,因此最初显示在屏幕上的图片数量也不同。您需要考虑正在使用的设备,并决定哪些资源应该预先加载,哪些资源应该懒加载。

另一个例子是,任何在初始加载时稍微超出视口的图片可能都不应该懒加载。这是根据上面讨论的原则——提前加载。因此,假设任何距离视口底部 500px 或一个滚动距离的图片也可以预先加载。

还有一个例子是,如果页面很短。它可能只有一个或两个滚动,或者可能视口外只有不到五张图片。在这些情况下,您可能可以完全省略懒加载。它不会在性能方面为最终用户提供任何显著的益处,并且您在页面上加载的额外 JavaScript 来启用懒加载将抵消您从中获得的任何潜在收益。

第 5 章:懒加载对 JavaScript 的依赖

懒加载的整个理念依赖于用户浏览器中启用了 JavaScript 并且可用。虽然大多数用户可能都启用了 JavaScript,但必须为 未启用 JavaScript 的情况做好计划。

您可以显示一条消息告诉用户为什么图片无法加载,并鼓励他们使用现代浏览器或启用 JavaScript。

另一种方法是使用 noscript 标签。但是,这种方法有一些需要注意的地方。这个 Stack Overflow 上的问题线程很好地解决了这些问题,建议任何希望解决此类用户问题的人阅读。

由于浏览器和设备之间的环境和实现细节可能有所不同,您可能希望考虑使用经过验证的懒加载库,而不是从头开始编写。

以下是一些流行的库和平台特定的插件列表,它们可以让您轻松实现懒加载

  • Yet Another Lazy Loader:此库使用 Intersection Observer API,并为尚不支持它的浏览器回退到基于事件的懒加载。这非常适合几乎任何 HTML 元素,但不幸的是,它不适用于 CSS 中的背景图片。好消息是它支持 IE 11 及更高版本。
  • lazysizes:这是一个非常流行的库,具有广泛的功能。它包括对响应式图片 srcsetsizes 属性的支持,并提供卓越的性能,即使它没有使用 Intersection Observer API。
  • WordPress A3 Lazy Load:有很多 WordPress 懒加载插件,但这个插件具有一套强大的功能,包括在 JavaScript 不可用时的回退。
  • jQuery Lazy:一个使用 jQuery 实现的简单库。
  • WeltPixel Lazy Loading Enhanced:Magento 2 扩展。
  • Magento Lazy Image Loader:另一个 Magento 扩展,适用于 1.x 版本。
  • Shopify Lazy Image Plugin(付费):在 Shopify 网站上启用懒加载。

第 7 章:测试懒加载

实现懒加载后,您可能希望检查它是否按预期工作。最简单的方法是在浏览器中打开开发者工具。

从那里,转到网络 > 图片。第一次刷新页面时,您应该只在列表中看到加载的图片。

然后,当您开始向下滚动页面时,其他图片加载请求将被触发并加载。您还可以在此视图的水瀑布列中注意到图片加载的计时。它将帮助您识别任何图片加载问题或触发图片加载的问题。

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

结论

我们已经全面了解了图片懒加载!如果实施得当,懒加载可以显著提升网站性能,同时降低整体页面大小和交付成本,这得益于它延迟加载不必要的资源。

所以,还在等什么?现在就开始使用图片懒加载吧!如果你需要回顾一下它的工作原理,可以保存以下信息图表。

Lazy loading infographic preview.
点击查看完整版本。