延迟加载图片和 <iframe> 元素

图片和 <iframe> 元素通常比其他类型的资源消耗更多带宽。对于 <iframe> 元素,加载和渲染其中的网页可能需要相当多的额外处理时间。

对于图片延迟加载,延迟加载初始视口之外的图片有助于减少初始视口内更关键资源的带宽争用。在网络连接较差的情况下,这有助于提高网页的 Largest Contentful Paint (LCP),重新分配的带宽有助于 LCP 候选元素更快地加载和呈现。

对于 <iframe> 元素,可以通过在启动期间延迟加载它们来提高网页的互动到下一次绘制 (INP)。这是因为 <iframe> 是一个完全独立的 HTML 文档,具有自己的子资源。虽然 <iframe> 元素可以在单独的进程中运行,但它们通常会与其他线程共享一个进程,这可能会导致网页对用户输入的响应速度变慢。

因此,延迟加载屏幕外图片和 <iframe> 元素是一种值得采用的技术,只需付出相当少的努力,就能获得相当不错的性能回报。本模块介绍了如何在网页的关键启动期间延迟加载这两种类型的元素,从而提供更快、更好的用户体验。

使用 loading 属性延迟加载图片

您可以向 <img> 元素添加 loading 属性,以告知浏览器应如何加载这些元素:

  • "eager" 会告知浏览器应立即加载图片,即使图片位于初始视口之外也是如此。这也是 loading 属性的默认值。
  • "lazy" 会延迟加载图片,直到图片位于距离可见视口一定距离内时才加载。此距离因浏览器而异,但通常设置得足够大,以便在用户滚动到相应图片时加载该图片。

另请注意,如果您使用的是 <picture> 元素,则 loading 属性仍应应用于其子级 <img> 元素,而不能应用于 <picture> 元素本身。这是因为 <picture> 元素是一个容器,其中包含指向不同候选图片的其他 <source> 元素,而浏览器选择的候选图片会直接应用于其子级 <img> 元素。

不要延迟加载初始视口中的图片

您应仅向位于初始视口之外的 <img> 元素添加 loading="lazy" 属性。不过,在网页呈现之前,很难知道元素在视口内的确切位置。必须考虑不同的视口大小、宽高比和设备。

例如,桌面设备的视口与手机上的视口可能大相径庭,因为桌面设备可以呈现更多垂直空间,因此可能能够在初始视口中显示在物理尺寸较小的设备的初始视口中无法显示的图片。以竖屏方向使用的平板电脑也会显示相当多的垂直空间,甚至可能比某些桌面设备还要多。

不过,在某些情况下,您应避免应用 loading="lazy",这一点非常明确。例如,在以下情况下,您应务必从 <img> 元素中省略 loading="lazy" 属性:主打图片或其他图片使用情形,其中 <img> 元素很可能会显示在任何设备的折叠线上方或布局顶部附近。对于可能成为 LCP 候选对象的图片,这一点尤为重要

延迟加载的图片需要等待浏览器完成布局,才能知道图片的最终位置是否在视口内。这意味着,如果可见视口中的 <img> 元素具有 loading="lazy" 属性,则只有在所有 CSS 下载、解析并应用于网页后,才会请求该元素,而不是在预加载扫描器在原始标记中发现该元素时立即提取该元素。

由于所有主要浏览器都支持 <img> 元素上的 loading 属性,因此无需使用 JavaScript 来延迟加载图片,因为向网页添加额外的 JavaScript 来提供浏览器已提供的功能会影响网页性能的其他方面,例如 INP。

图片延迟加载演示

延迟加载 <iframe> 元素

延迟加载 <iframe> 元素(直到它们在视口中可见)可以节省大量数据,并改进顶级网页加载所需的关键资源的加载。此外,由于 <iframe> 元素本质上是加载到顶级文档中的整个 HTML 文档,因此它们可以包含大量子资源(尤其是 JavaScript),如果这些框架中的任务需要大量处理时间,则可能会对网页的 INP 产生相当大的影响。

第三方嵌入内容是 <iframe> 元素的常见用例。例如,嵌入式视频播放器或社交媒体帖子通常使用 <iframe> 元素,并且它们通常需要大量子资源,这也会导致顶级网页的资源出现带宽争用。举例来说,延迟加载 YouTube 视频的嵌入内容可在初始网页加载期间节省超过 500 KiB 的数据,而延迟加载 Facebook “赞”按钮插件则可节省超过 200 KiB 的数据,其中大部分是 JavaScript。

无论哪种情况,只要网页上存在位于首屏下方的 <iframe>,您就应强烈考虑对其进行延迟加载(如果它不是必须预先加载的),因为这样做可以显著改善用户体验。

<iframe> 元素的 loading 属性

所有主流浏览器也支持 <iframe> 元素上的 loading 属性loading 属性的值及其行为与使用 loading 属性的 <img> 元素相同:

  • "eager" 为默认值。它会通知浏览器立即加载 <iframe> 元素的 HTML 及其子资源。
  • "lazy" 会延迟加载 <iframe> 元素的 HTML 及其子资源,直到该元素位于距离视口一定距离内时才加载。

延迟加载 iframe 演示

外墙

您可以根据用户互动情况按需加载嵌入内容,而不是在网页加载期间立即加载嵌入内容。为此,您可以显示图片或其他合适的 HTML 元素,直到用户与之互动为止。用户与元素互动后,您可以将其替换为第三方嵌入内容。这种技术称为外观

外观模式的常见应用场景是来自第三方服务的视频嵌入,其中嵌入可能涉及加载许多额外的且可能开销很大的子资源(例如 JavaScript),而不仅仅是视频内容本身。在这种情况下,除非视频有正当的自动播放需求,否则视频嵌入需要用户先点击播放按钮进行互动,然后才能播放。

这是一个绝佳的机会,可以展示与嵌入的视频在视觉上相似的静态图片,同时节省大量带宽。用户点击图片后,该图片会被实际的 <iframe> 嵌入代码替换,从而触发第三方 <iframe> 元素的 HTML 及其子资源开始下载。

除了改进初始网页加载之外,另一个主要优势是,如果用户从未播放过视频,则永远不会下载交付视频所需的资源。这是一个不错的模式,因为它可以确保用户只下载他们实际需要的内容,而不会对用户需求做出可能错误的假设。

聊天 widget 是外观模式的另一个出色应用场景。大多数聊天微件都会下载大量 JavaScript,这可能会对网页加载速度和对用户输入的响应速度产生不利影响。与预先加载任何内容一样,成本是在加载时产生的,但对于聊天微件,并非所有用户都永远不会与之互动。

另一方面,使用 facade 可以将第三方“开始聊天”按钮替换为虚假按钮。一旦用户与该占位符进行有意义的互动(例如将指针悬停在上面一段合理的时间,或点击该占位符),当用户需要时,实际的功能性聊天微件就会插入到相应位置。

虽然您当然可以自行构建外观,但对于更受欢迎的第三方,也有一些开源选项可供选择,例如用于 YouTube 视频的 lite-youtube-embed、用于 Vimeo 视频的 lite-vimeo-embed 以及用于聊天微件的 React Live Chat Loader

JavaScript 延迟加载库

如果您需要延迟加载 <video> 元素、<video> 元素 poster 图片、由 CSS background-image 属性加载的图片或其他不受支持的元素,可以使用基于 JavaScript 的延迟加载解决方案(例如 lazysizesyall.js)来实现,因为延迟加载这些类型的资源不是浏览器级功能。

尤其是,自动播放和循环播放的 <video> 元素(不含音轨)比使用 GIF 动画更高效,后者通常比具有同等视觉质量的视频资源大数倍。即便如此,这些视频在带宽方面仍可能占用大量资源,因此延迟加载它们是另一种优化方式,可大幅减少浪费的带宽。

这些库大多使用 Intersection Observer API(如果网页的 HTML 在初始加载后发生变化,还会使用 Mutation Observer API)来识别元素何时进入用户的视口。如果图片可见(或即将进入视口),则 JavaScript 库会将非标准属性(通常为 data-src 或类似属性)替换为正确的属性,例如 src

假设您有一个视频可以替换 GIF 动画,但您希望使用 JavaScript 解决方案对其进行延迟加载。您可以使用 yall.js 实现此目的,方法是使用以下标记模式:

<!-- The autoplay, loop, muted, and playsinline attributes are to
     ensure the video can autoplay without user intervention. -->
<video class="lazy" autoplay loop muted playsinline width="320" height="480">
  <source data-src="video.webm" type="video/webm">
  <source data-src="video.mp4" type="video/mp4">
</video>

默认情况下,yall.js 会观察所有符合条件的且具有 "lazy" 类的 HTML 元素。在网页上加载并执行 yall.js 后,视频不会加载,直到用户将其滚动到视口中。此时,<video> 元素的子元素 <source> 上的 data-src 属性会替换为 src 属性,这会发送下载视频的请求并自动开始播放视频。

知识测验

<img><iframe> 元素的 loading 属性的默认值是什么?

"lazy"
"eager"

何时适合使用基于 JavaScript 的延迟加载解决方案?

对于不支持 loading 属性的资源,例如旨在替换动画图片的自动播放视频,或者旨在延迟加载 <video> 元素的封面图片。
对于任何可以延迟加载的资源。

何时使用外观模式是一种有用的技巧?

对于任何第三方嵌入内容,如果加载所需的资源不仅非常多,而且很有可能并非所有用户都会与之互动。
对于任何消耗大量数据的第三方嵌入内容,无论用户是否需要。

接下来:预提取和预渲染

现在,您已经掌握了图片和 <iframe> 元素的延迟加载,可以更好地确保网页能够更快地加载,同时满足用户的需求。不过,在某些情况下,预先加载资源可能是有益的。在下一个模块中,您将了解预提取和预渲染,以及如何通过提前加载后续页面来大幅加快导航速度。