由于图片作为 <img>
元素或 CSS 背景图片内嵌在 HTML 中,因此可以显示在网页上。在本博文中,您将了解如何延迟加载这两种类型的图片。
内嵌图片
<img>
元素中使用的图片是最常见的延迟加载对象。使用内嵌图片时,我们有三种延迟加载选项,它们可组合使用以实现各种浏览器的最佳兼容性:
使用浏览器级延迟加载
Chrome 和 Firefox 都支持使用 loading
属性进行延迟加载。此属性可添加到 <img>
元素以及 <iframe>
元素。如果值为 lazy
,则指示浏览器在图片位于视口中时立即加载该图片,并在用户滚动到其他图片附近时提取其他图片。
如需详细了解浏览器支持,请参阅 MDN 的浏览器兼容性表的 loading
字段。如果浏览器不支持延迟加载,则系统会忽略该属性,并且图片会像往常一样立即加载。
对于大多数网站,向内嵌图片添加此属性可以提升性能,并减少用户加载他们可能永远不会滚动到的图片。如果您有大量图片,并且希望确保浏览器的用户不支持延迟加载,您需要将此方式与下面介绍的其中一种方法结合使用。
如需了解详情,请参阅适用于网页的浏览器级延迟加载。
使用 Intersection Observer
为了对 <img>
元素执行延迟加载,我们使用 JavaScript 来检查这些元素是否在视口中。如果已启用,系统会使用指向所需图片内容的网址填充其 src
(有时是 srcset
)属性。
如果您之前编写过延迟加载代码,那么您可能是使用 scroll
或 resize
等事件处理脚本完成任务的。虽然这种方法在各个浏览器之间兼容性最好,但现代浏览器提供了一种性能更高、效率更高的方法来通过 Intersection Observer API 检查元素可见性。
与依赖于各种事件处理脚本的代码相比,Intersection Observer 更易于使用和阅读,因为您只需注册一个观察器来监视元素,而无需编写冗长的元素可见性检测代码。现在您只需决定元素可见时要执行的操作即可。
我们假设您的 <img>
元素延迟加载了以下基本标记模式:
<img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load-1x.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" alt="I'm an image!">
您应重点关注此标记的三个相关部分:
class
属性,即您在 JavaScript 中选择元素时所用的属性。src
属性,用于引用页面首次加载时显示的占位图片。data-src
和data-srcset
属性,都是占位符属性,包含元素进入视口后所加载图片的网址。
现在,我们来看看如何在 JavaScript 中使用 Intersection Observer,通过以下标记模式延迟加载图片:
document.addEventListener("DOMContentLoaded", function() {
var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("lazy");
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// Possibly fall back to event handlers here
}
});
在文档的 DOMContentLoaded
事件中,此脚本会向 DOM 查询所有类为 lazy
的 <img>
元素。如果 Intersection Observer 可用,请创建一个新的观察器,以在 img.lazy
元素进入视口时运行回调。
Intersection Observer 适用于所有现代浏览器。
因此,将其用作 loading="lazy"
的 polyfill 可确保大多数访问者都可以使用延迟加载。
CSS 中的图片
虽然 <img>
标记是在网页上使用图片的最常用方法,但也可以通过 CSS background-image
属性(和其他属性)调用图片。浏览器级延迟加载不适用于 CSS 背景图片,因此如果您有可延迟加载的背景图片,则需要考虑其他方法。
与加载时不考虑可见性的 <img>
元素不同,CSS 中的图片加载行为是通过更多推测实现的。构建文档和 CSS 对象模型以及渲染树后,浏览器会先检查 CSS 如何应用于文档,然后再请求外部资源。如果浏览器确定涉及外部资源的 CSS 规则不适用于当前构建中的文档,则浏览器不会请求该文档。
这种推测行为可用于延迟 CSS 中图片的加载,具体方法是使用 JavaScript 确定元素何时位于视口内,然后将一个类应用于该元素,以应用调用背景图片的样式。这样,图片便会在需要时(而不是初始加载时)下载。我们以包含大型主打背景图片的元素为例:
<div class="lazy-background">
<h1>Here's a hero heading to get your attention!</h1>
<p>Here's hero copy to convince you to buy a thing!</p>
<a href="/buy-a-thing">Buy a thing!</a>
</div>
div.lazy-background
元素通常包含由某个 CSS 调用的主打背景图片。不过,在此延迟加载示例中,您可以通过向 div.lazy-background
元素添加位于视口中的 visible
类来隔离该元素的 background-image
属性:
.lazy-background {
background-image: url("hero-placeholder.jpg"); /* Placeholder image */
}
.lazy-background.visible {
background-image: url("hero.jpg"); /* The final image */
}
然后,使用 JavaScript 检查该元素是否在视口中(使用 Intersection Observer,此时,将 visible
类添加到 div.lazy-background
元素中,以加载图片):
document.addEventListener("DOMContentLoaded", function() {
var lazyBackgrounds = [].slice.call(document.querySelectorAll(".lazy-background"));
if ("IntersectionObserver" in window) {
let lazyBackgroundObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
lazyBackgroundObserver.unobserve(entry.target);
}
});
});
lazyBackgrounds.forEach(function(lazyBackground) {
lazyBackgroundObserver.observe(lazyBackground);
});
}
});
对 Largest Contentful Paint (LCP) 的影响
延迟加载是一项很好的优化措施,可将图片的加载推迟到实际需要的时间,从而减少启动期间的整体流量消耗和网络争用。这可以缩短启动时间,并通过减少图像解码所需的时间来减少在主线程上的处理。
不过,如果您过于急于使用延迟加载技术,该技术可能会对您网站的 Largest Contentful Paint LCP 产生负面影响。您应避免在启动期间延迟加载视口中的图片。
使用基于 JavaScript 的延迟加载器时,您需要避免延迟加载视口内图片,因为这些解决方案通常使用 data-src
或 data-srcset
属性作为 src
和 srcset
属性的占位符。这里的问题在于,系统会延迟加载这些图片,因为浏览器预加载扫描器在启动期间找不到它们。
即便是使用浏览器级延迟加载来延迟加载视口中的图片,也可能适得其反。对视口内图片应用 loading="lazy"
后,该图片会延迟到浏览器确定它位于视口内,这可能会影响网页的 LCP。
永不在启动期间延迟加载视口中可见的图片。该模式会对您网站的 LCP 产生负面影响,进而对用户体验产生负面影响。如果启动时需要图片,请在启动时尽快加载,而不要延迟加载!
延迟加载库
您应该尽可能使用浏览器级延迟加载,但如果您发现自己无法采用此方法(例如有大量用户仍依赖于旧版浏览器),则可以使用以下库延迟加载图片:
- lazysizes 是一个功能齐全的延迟加载库,用于延迟加载图片和 iframe。它使用的模式与此处显示的代码示例非常相似,因为它会自动绑定到
<img>
元素的lazyload
类,并要求您在data-src
和/或data-srcset
属性中指定图片网址,这两个属性的内容将分别交换到src
和/或srcset
属性中。它使用 Intersection Observer(可对它执行 polyfill),并且可通过许多插件进行扩展,以便执行延迟加载视频等操作。详细了解如何使用 lazysizes。 - vanilla-lazyload 是一个轻量级选项,用于延迟加载图片、背景图片、视频、iframe 和脚本。它利用 Intersection Observer,支持自适应图片,并支持浏览器级延迟加载。
- lozad.js 是另一个仅使用 Intersection Observer 的轻量级选项。因此,它的性能极佳,但需要先 polyfill 才能在旧版浏览器上使用。
- 如果您需要 React 专用的延迟加载库,请考虑使用 react-lazyload。虽然它不使用 Intersection Observer,但它为习惯于使用 React 开发应用的用户提供熟悉的图片延迟加载方法。