本文由语雀用户灯青翻译,转载请注明原作者及译者。
在这篇文章里,我们将介绍一个崭新的HTML属性——loading,它能为 <img>
和 <iframe>
提供原生的懒加载支持!为了满足你的好奇心,我们先来在实践中看看它是怎么用的。
<img src="celebration.jpg" loading="lazy" alt="..." />
<iframe src="video-player.html" loading="lazy"></iframe>
我们希望在Chrome 75 以上的版本中提供对 loding
属性的原生支持,而且我们正在为这一即将推出的特性做更进一步的工作。在它推出之前,我们还是先来看看 loading
标签是如何工作的吧!
介绍
网页通常会包含大量的图片,它们会导致流量大量消耗、页面臃肿,影响页面的加载速度。这些图片中的相当一部分都是离屏的,用户需要滚动页面才能看到它们。
在以前,为了减轻离屏图片对于页面加载时间的影响,开发者不得不使用一些JavaScript库(如LazySizes ),来实现当用户滚动页面到接近这些图片时再去加载它们。
一个页面加载了211个图片。上方没有使用懒加载的版本一次性加载了10MB的图片数据。下方的懒加载版本(使用LazySizes)只加载了250KB的数据——其它的图片在用户滚动的过程中再去加载。见WPT
如果浏览器能避免加载这些离屏的图片将会怎么样呢?这可以让在视口中的内容加载的更快,减少总体的网络流量消耗;在配置较低的设备中,还能减少内存消耗。好的,我很高兴能告诉你,这马上就要成为现实了。你可以使用全新的 loading
属性来为图片和框架提供浏览器级别的懒加载支持。
loading 属性
loading
属性允许浏览器直到用户滚动页面到离屏的图片和框架附近时再去延迟加载它们。 loading
属性有三个值:
- lazy:表示这个元素适合懒加载。
- eager:这个元素并不适合懒加载,请立即加载它。
- auto:由浏览器决定是否懒加载。
如果不明确指定一个值,那么会产生和 loading=auto
一样的效果。
<img>
和 <iframe>
的 loading
属性正在被计划纳入HTML标准 。
例子
loading
属性可以作用于 <img>
标签(包括与 srcset
同用以及位于 <picture>
内部的情况)和 <iframe>
标签
<!-- 直到用户滚动页面到图片附近再去懒加载它 -->
<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不用写loading属性 -->
<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 src="video-player.html" loading="lazy"></iframe>
到底什么时候才是“滚动页面到图片附近”呢?这其实是由浏览器决定的。通常来说,我们希望浏览器能在图片和框架真正滚动到视口内之前一点时去加载它们,这样当它们真正滚动到视口内时,大概率已经被加载完了,从而提升用户体验。
Note:我认为 我们把这个属性命名为 loading
是因为这样的命名方式与 decoding 属性的命名方式更相符。之前的一些提议,比如命名为 lazyload
,都是不合适的,因为我们需要让它支持多个值( lazy
, eager
和 auto
)
译者注:这一段实际上在讨论为什么把这个属性叫做loading。作者在github中的issue中说: HTMLImageElement元素有decoding属性,是不是也暗示着loading属性应该也可以用?loading属性不会和
的load()方法冲突,而且一些开发者认为loading比loadpolicy更直观。
特性检测
为了能够做跨浏览器支持,我们一直很重视给予开发者去引入一个懒加载JavaScript库的时机。你可以使用如下方式检测浏览器是否支持 loading
属性:
if ('loading' in HTMLImageElement.prototype) {
// 浏览器支持loading属性
} else {
// 引入一个js库来实现懒加载
}
Note:你也可以使用“渐进增强”的思路来应用 loading
属性。支持这个属性的浏览器可以通过设置 loading=lazy
来获得新的懒加载行为,而不支持的浏览器就直接加载图片。
跨浏览器的图片懒加载
如果跨浏览器的图片懒加载支持是很重要的,在你使用 <img src=a.jpg loading=lazy/>
这样的标记时,那么仅仅对 loading
属性进行特性检测并决定是否引入一个js库是不够的。
标记需要使用像 <img data-src=a.jpg>
这样的形式(而不是 src
、 srcset
或者 <source>
)来避免当浏览器不支持 loading
属性时,图片被立即加载。可以使用JavaScript,在 loading
被支持时把这些属性变成合适的属性(src),在 loading
不被支持时引入一个JavaScript库,
下面是一个小例子,来展示一下上面所说的情况。
- 在视口中的图片是普通的
<img>
标签。如果使用data-src
的话,预加载扫描时就会忽略它,这样即使在支持loading
属性的浏览器中也无法实现懒加载。所以我们把视口中的图片写成普通的标签就好。 - 我们使用
data-src
来避免在不支持loading
的浏览器中图片被立即加载。如果浏览器支持loading
的话,我们把data-src
转化为src
属性。 - 如果浏览器不支持
loading
属性,我们加载一个作为回退措施的JavaScript库(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 (reads data-src & class=lazyload)
lazySizes.init(); // lazySizes works off a global.
}
})();
</script>
Demo
这里有一个使用loading=lazy加载100个小猫猫照片的demo ,赶紧点开看看呀!
如果你的浏览器不支持 loading
属性,你可以看看下面的视频~
Chrome的实现细节
我们强烈建议等我们发布了稳定版之后再把它用在生产环境。如果你是一个尝鲜者,你可以看看下面这些东西,我们相信会对你有帮助的。
**
开启这个功能
进入 chrome://flags
,打开 Enable lazy frame loading
和 Enable lazy image loading
两个开关,然后重启Chrome。
配置
这里,为了方便说明,我们把懒加载的框架和图片开始加载时距离视口的距离称为“加载阈值”
Chrome原生懒加载的实现不仅基于当前滚动位置,还基于网络连接的速度。图片和框架的加载阈值会根据网络连接速度而产生变化,这些规则都是硬编码的,但是可以通过命令行命令重写。这是一个重写懒加载设置的小例子:
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,那么在任何网络情况下,当图片和框架距离视口400px以内时开始加载它们。下面的例子中,我们把各种情况下加载阈值设置为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'
在未来的几周,随着我们实现方案的逐步稳定,这些默认值很有可能会变化。
开发者工具
loading
属性有一个有趣的实现细节,在浏览器加载页面时,会先获取图片开头的2KB数据。如果服务器支持范围请求(range request),开头的2KB数据很可能会包含图片的大小信息。这让我们可以生成并展示一个相同大小的占位元素。对于像图标这样的小图片,开头的2KB很可能就是整张图片了。
当用户即将看到这些图片时,Chrome会获取图片剩下的字节。这里需要警告你一下,使用loading属性会导致Chrome DevTool:
- 在Network面板中,Chrome似乎获取了这些图片两次。
- Resource Timing中,每个图片会有两个请求
在服务器端确定是否支持loading属性
在一个完美的世界里,你不需要用JavaScript去检测浏览器是否支持loading然后去考虑是否回退到其它JavaScript库,你完全可以在提供HTML前就确定好是否提供带着JavaScript库的HTML文件。你可以使用客户端暗示(Client Hint)来实现这样的检查。
我们正在考虑设计一个用于传达 loading
偏好的客户端暗示,但是目前还仅仅是在讨论阶段。
引用文献
感谢 Simon Pieters, Yoav Weiss 和 Mathias Bynens 提供的反馈。衷心感谢 Ben Greenstein, Scott Little, Raj T 和 Houssein Djirdeh 为 Lazyload所作出的贡献.
原文链接:HERE
作者:Addy Osmani