现代图片性能优化及体验优化指南 - 懒加载及异步图像解码方案

时间:2021-12-12 00:34:47

本文是系列第四篇。系列文章:

  1. 现代图片性能优化及体验优化指南 - 图片类型及 Picture 标签的使用
  2. 现代图片性能优化及体验优化指南 - 响应式图片方案
  3. 现代图片性能优化及体验优化指南 - # 缩放精细化展示及避免布局偏移、拉伸

图片资源,在我们的业务中可谓是占据了非常大头的一环,尤其是其对带宽的消耗是十分巨大的。

对图片的性能优化及体验优化在今天就显得尤为重要。本文,就将从各个方面阐述,在各种新特性满头飞的今天,我们可以如何尽可能的对我们的图片资源,进行性能优化及体验优化。

懒加载/异步图像解码方案

继续下一个章节。本章节,我们来讨论下图片的懒加载与异步图像解码方案。

图片的懒加载

懒加载是一种网页性能优化的常见方式,它能极大的提升用户体验。到今天,现在一张图片超过几 M 已经是常见事了。如果每次进入页面都需要请求页面上的所有的图片资源,会较大的影响用户体验,对用户的带宽也是一种极大的损耗。

所以,图片懒加载的意义即是,当页面未滚动到相应区域,该区域内的图片资源(网络请求)不会被加载。反之,当页面滚动到相应区域,相关图片资源的请求才会被发起。

在过去,我们通常都是使用 JavaScript 方案进行图片的懒加载。而今天,我们在图片的懒加载实现上,有了更多不一样的选择。

JavaScript 方案实现图片的懒加载

首先,回顾一下过往最常见的,使用 JavaScript 方案实现图片的懒加载。

通过 JavaScript 实现的懒加载,主要是两种方式:

  1. 监听 onscroll 事件,通过 getBoundingClientRect API 获取元素图片距离视口顶部的距离,配合当前可视区域的位置实现图片的懒加载
  2. 通过 HTML5 的 IntersectionObserver API,Intersection Observer(交叉观察器) 配合监听元素的 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

content-visibility: auto VS 图片懒加载

当然,其实使用 content-visibility: auto 并不能真正意义上实现图片的懒加载。

这是因为,即便当前页面可视区域外的内容未被渲染,但是图片资源的 HTTP/HTTPS 请求,依然会在页面一开始被触发!

因此,这也得到了一个非常重要的结论:

content-visibility: auto 无法直接替代图片懒加载,设置了 content-visibility: auto 的元素在可视区外只是未被渲染,但是其中的静态资源仍旧会在页面初始化的时候被全部加载。因此,它更像是一个虚拟列表的替代方案。

关于 content-visibility 本文限于篇幅,没有完全展开,但是它是一个非常有意思且对渲染性能有帮助的属性,完整的教程,你可以看我的这篇文章 -- 使用 content-visibility 优化渲染性能

使用 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 接口的 decoding 属性用于告诉浏览器使用何种方式解析图像数据。

它的可选取值如下:

  • sync: 同步解码图像,保证与其他内容一起显示。
  • async: 异步解码图像,加快显示其他内容。
  • auto: 默认模式,表示不偏好解码模式。由浏览器决定哪种方式更适合用户。

上文其实也提及了,浏览器在进行图片渲染展示的过程中,是需要对图片文件进行解码的,这一个过程快慢与图片格式有关。

而如果我们不希望图片的渲染解码影响页面的其他内容的展示,可以使用 decoding=async 选项,像是这样:

<img src="xxx.png" decoding="async">

这样,浏览器便会异步解码图像,加快显示其他内容。这是图片优化方案中可选的一环。

同样的,我们来看看到今天(2023-02-26),decoding="async" 的兼容性,整体还是非常不错的,作为渐进增强方案使用,是非常好的选择。

现代图片性能优化及体验优化指南 - 懒加载及异步图像解码方案

实际检验 loading=lazydecoding=async 效果

OK,下面我们制作一个简单的 DEMO,试一下 loading=lazydecoding=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=lazydecoding=async 的状态下,看看 Network 的表现:

现代图片性能优化及体验优化指南 - 懒加载及异步图像解码方案

我这里没有模拟弱网环境,网速非常快,可以看到,发送了 339 个图片资源请求,也就是全部的图片资源在页面加载的过程中都请求了,页面 Load 事件完成的时间为 1.28s。

好,我们给所有的图片元素,添加上 loading=lazydecoding=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" 的作用,只是由于是纯图片页面,效果不那么明显。感兴趣的同学,可以自行尝试。

总结一下

在本章节中,我们介绍了不同的方式实现图片的懒加载、延迟渲染、异步解码,它们分别是:

  1. 通过 onscroll 事件与 getBoundingClientRect API 实现图片的懒加载方案
  2. 通过 Intersection Observer(交叉观察器)实现比监听 onscroll 性能更佳的图片懒加载方案
  3. 通过 content-visibility: auto 实现图片资源的延迟渲染
  4. 通过 loading=lazy HTML 属性实现图片懒加载
  5. 通过 decoding=async HTML 属性实现图片的异步解码

当然,本文是现代图片性能优化及体验优化指南的第四篇,后续将给大家带来图片优化的最后一个章节:

  • 可访问性 & 图片资源的容错及错误处理

感兴趣的可以提前关注。

最后

OK,本文到此结束,希望本文对你有所帮助 ????

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻 ????

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。