现代图片性能优化及体验优化指南 - 懒加载及异步图像解码方案
本文是系列第四篇。系列文章:
现代图片性能优化及体验优化指南 - 图片类型及 Picture 标签的使用[1] 现代图片性能优化及体验优化指南 - 响应式图片方案[2] 现代图片性能优化及体验优化指南 - # 缩放精细化展示及避免布局偏移、拉伸[3]
图片资源,在我们的业务中可谓是占据了非常大头的一环,尤其是其对带宽的消耗是十分巨大的。
对图片的性能优化及体验优化在今天就显得尤为重要。本文,就将从各个方面阐述,在各种新特性满头飞的今天,我们可以如何尽可能的对我们的图片资源,进行性能优化及体验优化。
懒加载/异步图像解码方案
继续下一个章节。本章节,我们来讨论下图片的懒加载与异步图像解码方案。
图片的懒加载
懒加载是一种网页性能优化的常见方式,它能极大的提升用户体验。到今天,现在一张图片超过几 M 已经是常见事了。如果每次进入页面都需要请求页面上的所有的图片资源,会较大的影响用户体验,对用户的带宽也是一种极大的损耗。
所以,图片懒加载的意义即是,当页面未滚动到相应区域,该区域内的图片资源(网络请求)不会被加载。反之,当页面滚动到相应区域,相关图片资源的请求才会被发起。
在过去,我们通常都是使用 JavaScript 方案进行图片的懒加载。而今天,我们在图片的懒加载实现上,有了更多不一样的选择。
JavaScript 方案实现图片的懒加载
首先,回顾一下过往最常见的,使用 JavaScript 方案实现图片的懒加载。
通过 JavaScript 实现的懒加载,主要是两种方式:
监听 onscroll 事件,通过 getBoundingClientRect
API 获取元素图片距离视口顶部的距离,配合当前可视区域的位置实现图片的懒加载通过 HTML5 的 IntersectionObserver
API,Intersection Observer(交叉观察器)[4] 配合监听元素的isIntersecting
属性,判断元素是否在可视区内,能够实现比监听 onscroll 性能更佳的图片懒加载方案
但是,JavaScript 方案的一个劣势在于,不管如何,需要引入一定量的 JavaScript 代码,进行一定量的运算。
到今天,其实我们有更多的其他便捷的方式去实现图片的懒加载。
使用 content-visibility: auto
实现图片内容的延迟渲染
首先,介绍一个非常有用,但是相对较为冷门的属性 -- content-visibility
。
content-visibility
:属性控制一个元素是否渲染其内容,它允许用户代理(浏览器)潜在地省略大量布局和渲染工作,直到需要它为止。
利用 content-visibility
的特性,我们可以实现如果该元素当前不在屏幕上,则不会渲染其后代元素。
假设我们有这样一个 DEMO:
<div class="g-wrap">
// 模块 1
<div class="paragraph">
<p>Lorem Start!</p>
<img src="https://s1.ax1x.com/2023/02/20/pSX1xMV.png" alt="" />
<p>Lorem End!</p>
</div>
// 模块 2
<div class="paragraph">
<p>Lorem Start!</p>
<img src="https://s1.ax1x.com/2023/02/20/pSX1xMV.png" alt="" />
<p>Lorem End!</p>
</div>
// ... 连续几十个上述类似的结构
</div>
只需要给需要延迟(实时)渲染的元素,设置简单的 CSS 样式:
.paragraph {
content-visibility: auto;
}
我们来看一下,设置了 content-visibility: auto
与没设置的区别。
如果,不添加上述的 content-visibility: auto
代码,页面的滚动条及滚动效果如下:
那么,在添加了 content-visibility: auto
之后,注意观察页面的滚动条及滚动效果:
可以看到滚动条在向下滚动在不断的抽搐,这是由于下面不在可视区域内的内容,一开始是没有被渲染的,在每次滚动的过程中,才逐渐渲染,以此来提升性能。
Codepen Deom -- content-visibility: auto Image Load Demo[5]
content-visibility: auto
VS 图片懒加载
当然,其实使用 content-visibility: auto
并不能真正意义上实现图片的懒加载。
这是因为,即便当前页面可视区域外的内容未被渲染,但是图片资源的 HTTP/HTTPS 请求,依然会在页面一开始被触发!
因此,这也得到了一个非常重要的结论:
content-visibility: auto
无法直接替代图片懒加载,设置了 content-visibility: auto
的元素在可视区外只是未被渲染,但是其中的静态资源仍旧会在页面初始化的时候被全部加载。因此,它更像是一个虚拟列表的替代方案。
关于
content-visibility
本文限于篇幅,没有完全展开,但是它是一个非常有意思且对渲染性能有帮助的属性,完整的教程,你可以看我的这篇文章 -- 使用 content-visibility 优化渲染性能[6]
使用 loading=lazy
HTML 属性实现图片懒加载
OK,content-visibility
很不错,但是略有瑕疵。但是,我们还有其他方式。
HTML5 新增了一个 loading
属性。
到今天,除了 IE 系列浏览器,目前都支持通过 loading
属性实现延迟加载。此属性可以添加到 <img>
元素中,也可以添加到 <iframe>
元素中。
属性的值为 loading=lazy
会告诉浏览器,如果图像位于可视区时,则立即加载图像,并在用户滚动到它们附近时获取其他图像。
我们可以像是这样使用它:
<img src="xxx.png" loading="lazy">
这样,便可以非常便捷的实现图片的懒加载,省去了添加繁琐的 JavaScript 代码的过程。
看看 loading=lazy
到今天(2023-02-26)的兼容性,还是非常不错的:
使用 decoding=async
实现图片的异步解码
除了 loading=lazy
,HTML5 还新增了一个非常有意思的属性增强图片的用户体验。那就是 decoding
属性。
HTMLImageElement[7] 接口的 decoding
属性用于告诉浏览器使用何种方式解析图像数据。
它的可选取值如下:
sync
: 同步解码图像,保证与其他内容一起显示。async
: 异步解码图像,加快显示其他内容。auto
: 默认模式,表示不偏好解码模式。由浏览器决定哪种方式更适合用户。
上文其实也提及了,浏览器在进行图片渲染展示的过程中,是需要对图片文件进行解码的,这一个过程快慢与图片格式有关。
而如果我们不希望图片的渲染解码影响页面的其他内容的展示,可以使用 decoding=async
选项,像是这样:
<img src="xxx.png" decoding="async">
这样,浏览器便会异步解码图像,加快显示其他内容。这是图片优化方案中可选的一环。
同样的,我们来看看到今天(2023-02-26),decoding="async"
的兼容性,整体还是非常不错的,作为渐进增强方案使用,是非常好的选择。
实际检验 loading=lazy
与 decoding=async
效果
OK,下面我们制作一个简单的 DEMO,试一下 loading=lazy
与 decoding=async
的效果。
我们准备一个拥有 339 个图片的 HTML 页面,每个图片文件的 src 大小不一。
<div class="g-container">
<img src="image1.jpeg">
<img src="image2.jpeg">
// ... 339 个
</div>
CSS 的设置也很重要,由于是纯图片页面,如果不给图片设置默认高宽,最页面刷新的一瞬间,<img>
元素的高宽都是 0,会导致所有 <img>
元素都在可视区内,所以,我们需要给 <img>
设置一个默认的高宽:
img {
margin: 8px;
width: 300px;
height: 200px;
object-fit: cover;
}
这样,再不添加 loading=lazy
与 decoding=async
的状态下,看看 Network
的表现:
我这里没有模拟弱网环境,网速非常快,可以看到,发送了 339 个图片资源请求,也就是全部的图片资源在页面加载的过程中都请求了,页面 Load
事件完成的时间为 1.28s。
好,我们给所有的图片元素,添加上 loading=lazy
与 decoding=async
:
<div class="g-container">
<img src="image1.jpeg" loading="lazy" decoding="async">
<img src="image2.jpeg" loading="lazy" decoding="async">
// ... 339 个
</div>
看看效果:
可以看到,这一次只发送了 17 个图片资源请求,页面 Load
事件完成的时间为 26ms。
优化前 | 优化后 |
---|---|
1.28s | 26 ms |
1.28s 到 26ms,效果是非常明显的,如果是弱网环境,对首屏加载性能的提升,会更为明显!
当然,实际我测试的过程也,也单独试过 decoding="async"
的作用,只是由于是纯图片页面,效果不那么明显。感兴趣的同学,可以自行尝试。
总结一下
在本章节中,我们介绍了不同的方式实现图片的懒加载、延迟渲染、异步解码,它们分别是:
通过 onscroll 事件与 getBoundingClientRect
API 实现图片的懒加载方案通过 Intersection Observer(交叉观察器)实现比监听 onscroll 性能更佳的图片懒加载方案 通过 content-visibility: auto
实现图片资源的延迟渲染通过 loading=lazy
HTML 属性实现图片懒加载通过 decoding=async
HTML 属性实现图片的异步解码
当然,本文是现代图片性能优化及体验优化指南的第四篇,后续将给大家带来图片优化的最后一个章节:
可访问性 & 图片资源的容错及错误处理
感兴趣的可以提前关注。
最后
OK,本文到此结束,希望本文对你有所帮助 :)
更多精彩 CSS 技术文章汇总在我的 Github -- iCSS[8] ,持续更新,欢迎点个 star 订阅收藏。
如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。
参考资料
现代图片性能优化及体验优化指南 - 图片类型及 Picture 标签的使用: https://juejin.cn/post/7198182873366888509
[2]现代图片性能优化及体验优化指南 - 响应式图片方案: https://juejin.cn/post/7202266382841495611
[3]现代图片性能优化及体验优化指南 - # 缩放精细化展示及避免布局偏移、拉伸: https://juejin.cn/post/7203012367180185661
[4]Intersection Observer(交叉观察器): https://web.dev/lazy-loading-images/#images-inline-intersection-observer
[5]Codepen Deom -- content-visibility: auto Image Load Demo: https://codepen.io/Chokcoco/pen/poOEXZb
[6]使用 content-visibility 优化渲染性能: https://github.com/chokcoco/iCSS/issues/185
[7]HTMLImageElement: https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLImageElement
[8]Github -- iCSS: https://github.com/chokcoco/iCSS