[译] 原生实现图片懒加载

在本文中,咱们将研究新的 loading 属性,它为 <img><iframe> 带来了延迟加载的能力。若是你对此感兴趣,可查看如下示例:javascript

<img src="celebration.jpg" loading="lazy" alt="..." />
<iframe src="video-player.html" loading="lazy"></iframe>
复制代码

咱们但愿在 ~Chrome 75 中为 loading 提供支持,而且咱们正在深刻研究即将发布的新特性。在此以前,让咱们深刻了解它的工做原理。php

简介

Web 页面一般包含大量的图片,这些图片将影响网络流量、页面尺寸及页面加载速度。这些图片中许多处于屏幕外,每每须要用户滚动页面才能看到。html

过去,为了下降屏幕外的图片对页面加载时间的影响,开发人员不得不使用 JavaScript 库(好比:LazySizes)来推迟这些图片的加载时机,直到用户将页面滚动到它们附近。前端

页面加载 211 张图片。没有延迟加载的版本加载了 10 MB 数据。延迟加载版本(使用 LazySizes)仅预先加载了 250 KB 数据 - 其余图片将随着用户的滚动而加载。查看 WPTjava

若是浏览器就能帮你作到避免加载屏幕外的图片呢?这将有助于加快视窗中内容的加载、减小总体网络数据量以及低端设备下内存使用量。所以,我很高兴地告诉你们,很快就可使用 image 和 iframe 的新属性 loading 来实现了。android

loading 属性

loading 属性容许浏览器推迟加载屏幕外的 image 和 iframe 直到用户将页面滚动到它们附近。loading 支持三个值:ios

  • lazy:延迟加载。
  • eager:当即加载。
  • auto:由浏览器来决定是否延迟加载。

若是不指定该属性,其默认值为 auto。git

HTML 标准 正在研究将 <img><iframe>loading 属性做为标准的一部分。github

例子

loading 属性适用于 <img>(包括包含 srcset 属性及位于 <picture> 内部)和 <iframe>web

<!-- 延迟加载屏幕外的图片直到用户滚动到它附近 -->
<img src="unicorn.jpg" loading="lazy" alt=".."/>

<!-- 当即加载(而非延迟加载)图片 -->
<img src="unicorn.jpg" loading="eager" alt=".."/>

<!-- 浏览器决定是否延迟加载图片 -->
<img src="unicorn.jpg" loading="auto" alt=".."/>

<!-- 延迟加载 <picture> 内的图片。<img> 用来驱动图片的加载,所以 <picture> 及 srcset 会将合适的图片呈如今 <img> 上 -->
<picture>
  <source media="(min-width: 40em)" srcset="big.jpg 1x, big-hd.jpg 2x">
  <source srcset="small.jpg 1x, small-hd.jpg 2x">
  <img src="fallback.jpg" loading="lazy">
</picture>

<!-- 延迟加载设定 srcset 属性的图片-->
<img src="small.jpg" srcset="large.jpg 1024w, medium.jpg 640w, small.jpg 320w" sizes="(min-width: 36em) 33.3vw, 100vw" alt="A rad wolf" loading="lazy">

<!-- 延迟加载屏幕外的 iframe 直到用户滚动到它附近 -->
<iframe src="video-player.html" loading="lazy"></iframe>
复制代码

由浏览器完成“当用户滚动到附近时”的确切检测。通常来讲,咱们但愿浏览器在快要进入视窗口以前便开始提取延迟图片和 iframe 的内容。这将增长图片或 iframe 在用户滚动到它们时完成加载的更改。

注意:我曾建议应该将 loading 属性值做为属性名称,由于它的命名与 decoding 属性较为接近。在以前的提议中,相似 lazyload 这样的属性没有被接受,这是由于咱们须要支持多个值(lazyeagerauto)。

特性检测

咱们已知道为延迟加载(跨浏览器支持)获取及应用 JavaScript 库的重要性。loading 的支持状况能够经过如下方式进行检测:

<script> if ('loading' in HTMLImageElement.prototype) { // 浏览器支持 `loading`.. } else { // 获取并应用 polyfill/JavaScript 类库 // 来替代 lazy-loading。 } </script>
复制代码

注意:你还可使用 loading 做为一种渐进的加强功能。支持该属性的浏览器可经过 loading=lazy 得到新的延迟加载能力,不支持该属性的浏览器仍然会加载图片。

跨浏览器的图片延迟加载

若是跨浏览器支持图片的延迟加载很是重要,那么仅仅在使用 <img src=unicorn.jpg loading=lazy /> 的标记中进行特性检测、使用延迟加载库是不够的。

该标记需使用相似 <img data-src=unicorn.jpg />(而非 srcsrcset<source>)的属性,以免在不支持新属性的浏览器下触发马上加载。若是浏览器支持 loading,可使用 JavaScript 将这些属性更改成正确的属性,不然加载类库。

下面是一个能够说明其多是什么样子的例子。

  • 视窗/一屏展现图片是常规的 <img> 标签。data-src 会破坏预加载扫描程序,所以咱们但愿避免它出如今视窗中的全部内容中。
  • 咱们在图片上使用 data-src 以免在不支持的浏览器中触发马上加载,若是浏览器支持 loading,咱们将 data-src 替换为 src
  • 若是 loading 不被支持,咱们加载一个后备(LazySizes)脚本并启动它。在这里,咱们用 class=lazyload 向 LazySizes 指出,哪些图片要延迟加载。
<!-- 让咱们在视窗内正常加载这个图片 -->
<img src="hero.jpg" alt=".."/>

<!-- 让咱们以延迟加载的方式加载剩余图片 -->
<img data-src="unicorn.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="cats.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="dogs.jpg" loading="lazy" alt=".." class="lazyload"/>

<script> (async () => { if ('loading' in HTMLImageElement.prototype) { const images = document.querySelectorAll("img.lazyload"); images.forEach(img => { img.src = img.dataset.src; }); } else { // 动态引入 LazySizes 库 const lazySizesLib = await import('/lazysizes.min.js'); // 初始化 LazySizes(读取 data-src & class=lazyload) lazySizes.init(); // lazySizes 在全局环境下工做。 } })(); </script>
复制代码

示例

看看这个!一个 loading=lazy 示例,展现了整整 100 张小猫图片

详见 YouTube 视频:youtu.be/bhnfL6ODM68

Chrome 实现细节

咱们强烈建议等到 loading 属性处于稳定版本后再在你的生产环境中使用它。早期测试人员可能会发现如下注解很是有用。

马上尝试

转到 chrome://flags 并同时开启 "Enable lazy frame loading" 和 "Enable lazy image loading",而后从新启动 Chrome。

配置

Chrome 延迟加载的实现不只仅基于当前滚动位置的接近程度,还取决于网络链接速度。对于不一样的网络链接速度,延迟加载 frame 和图片的视窗距离阈值是硬编码的,能够经过命令行覆盖该值。如下是一个覆盖图片延迟加载设置的示例:

canary --user-data-dir="$(mktemp -d)" --enable-features=LazyImageLoading --blink-settings=lazyImageLoadingDistanceThresholdPxUnknown=5000,lazyImageLoadingDistanceThresholdPxOffline=8000,lazyImageLoadingDistanceThresholdPxSlow2G=8000,lazyImageLoadingDistanceThresholdPx2G=6000,lazyImageLoadingDistanceThresholdPx3G=4000,lazyImageLoadingDistanceThresholdPx4G=3000 'https://mathiasbynens.be/demo/img-loading-lazy'
复制代码

以上命令对应于(当前)默认配置。将全部值更改成 400 以便仅在滚动位置在距离图片的 400 像素之内开始延迟加载。下面咱们还能够看到距离阈值设为 1 像素的另外一个作法(在本文前面的视频中使用):

canary --user-data-dir="$(mktemp -d)" --enable-features=LazyImageLoading --blink-settings=lazyImageLoadingDistanceThresholdPxUnknown=1,lazyImageLoadingDistanceThresholdPxOffline=1,lazyImageLoadingDistanceThresholdPxSlow2G=1,lazyImageLoadingDistanceThresholdPx2G=1,lazyImageLoadingDistanceThresholdPx3G=1,lazyImageLoadingDistanceThresholdPx4G=1 'https://mathiasbynens.be/demo/img-loading-lazy'
复制代码

因为实如今将来几周内稳定下来,咱们的默认配置极可能会发生变化。

DevTools

Chrome 中 loading 的一个实现细节是它会在页面加载时获取前 2 KB 的图片数据。若是服务器支持范围请求,则前 2 KB 可能包含图片尺寸。这使得咱们可以生成/显示具备相同尺寸的占位符。若是像是图标一类的资源的话,前 2 KB 也颇有可能包含整幅图片了。

Chrome 会在用户即将看到图片时抓取其剩余数据。Chrome DevTools 中要注意的地方是,这可能致使(1)在 DevTools 的网络面板中“出现” 两次获取和(2)为每一个图片提供两个请求的资源定时。

服务端肯定 loading 支持

在一个美好的世界中,你不须要依赖客户端上的 JavaScript 特性检测来决定是否须要加载兼容库 — 你须要在提供包含 JavaScript 延迟加载库的 HTML 以前处理此问题。客户端提示能够启用此类检查。

传递 loading 参数的提示已经被考虑,但目前正处于早期讨论阶段。

总结

试试看 <img loading>,并让咱们知道你的想法。我对你们如何探索跨浏览器的经验,及是否有任何咱们错过的边缘状况特别感兴趣。

参考资料

感谢 Simon Pieters、Yoav Weiss 和 Mathias Bynens 的反馈。很是感谢 Ben Greenstein、Scott Little、Raj T 和 Houssein Djirdeh 在 LazyLoad 上的工做。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索