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

原文地址:Native image lazy-loading for the web!
原文作者:addyosmani
译文出自:掘金翻译计划
本文永久链接:github.com/xitu/gold-m…
译者:nanjingboy
校对者:xionglong58, portandbridge


在本文中,我们将研究新的 loading 属性,它为 <img> 及 `frameLabelStart--frameLabelEnd


我们希望在[ ~Chrome 75](https://link.juejin.im/?target=https%3A%2F%2Fchromestatus.com%2Ffeature%2F5645767347798016) 中为 loading 提供支持,并且我们正在深入研究即将发布的新特性。在此之前,让我们深入了解它的工作原理。

##简介

Web 页面通常包含大量的图片,这些图片将影响网络流量、页面尺寸及页面加载速度。这些图片中许多处于屏幕外,往往需要用户滚动页面才能看到。
过去,为了降低屏幕外的图片对页面加载时间的影响,开发人员不得不使用 JavaScript 库(比如:LazySizes)来推迟这些图片的加载时机,直到用户将页面滚动到它们附近。

<p style="text-align:center">![image.png](https://ucc.alicdn.com/pic/developer-ecology/79b0d637ed03496c904f38ce3bbeeb2f.png)</p>

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

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

##loading 属性

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



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



如果不指定该属性,其默认值为 auto。

![image.png](https://ucc.alicdn.com/pic/developer-ecology/ca3483e5249b4f969f395823d3b94406.png)

[HTML 标准](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fwhatwg%2Fhtml%2Fpull%2F3752) 正在研究将 `<img>` 和 `frameLabelStart--frameLabelEnd 

由浏览器完成“当用户滚动到附近时”的确切检测。一般来说,我们希望浏览器在快要进入视窗口之前便开始提取延迟图片和 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 上的工作。


作者:泪已无痕
链接:https://juejin.im/post/5cc183436fb9a032363936c3
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上一篇:1565: [NOI2009]植物大战僵尸 - BZOJ


下一篇:eBCC性能分析最佳实践(0) - 开启性能分析新篇章