首页 > 其他分享 >现代图片性能优化及体验优化指南 - 懒加载及异步图像解码方案

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

时间:2023-02-28 09:22:53浏览次数:42  
标签:异步 解码 页面 visibility content 图片 优化 加载

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

  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,本文到此结束,希望本文对你有所帮助

标签:异步,解码,页面,visibility,content,图片,优化,加载
From: https://www.cnblogs.com/coco1s/p/17162742.html

相关文章

  • Python 异步: 保护任务免于取消(13)
    Asyncio任务可以通过调用它们的cancel()方法来取消。我们可以通过将任务包装在对asyncio.shield()的调用中来保护任务不被取消。让我们仔细看看。1.什么是Asyncio......
  • JVM系统优化实践(5):什么时候GC以及有哪些GC
    您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~   既然程序运行会产生大量的废弃物,也就是「垃圾」,那总不能一直堆着不管吧。现在就来粗浅地谈谈Java里面什么时候会......
  • 记一次前端请求与响应包全加密的解码过程
    即上次解密后,开发不死心,过了几个月,给返回包也进行了加密。并对前端js进行了压缩混淆   根据观察,初步认为服务端也进行了相同的rsa+aes加密,然后把rsa加密后的key和i......
  • javascript尾递归优化
    JS中的递归我们来看一个阶乘的代码functionfoo(n){if(n<=1){return1;}returnn*foo(n-1);}foo(5);//120下面分析一下,代码运行过程中,......
  • NSGA2多目标优化算法的MATLAB仿真
    1.算法描述       首先将一群具有多个目标的个体(解集,或者说线代里的向量形式)作为父代初始种群,在每一次迭代中,GA操作后合并父代于自带。通过非支配排序,我们将所有个......
  • Java开发中要避免的坑和一些代码优化技巧
    1:动态SQL遇到的坑,先看下面OGNL表达式的说明。Anyobjectcanbeusedwhereabooleanisrequired.OGNLinterpretsobjectsasbooleanslikethis:Iftheobjecti......
  • 917~920 异步提交表单,Servlet,Dao,Servlic代码实现
    异步提交表单在此使用异步提交表单是为了获取服务器响应的数据。因为我们前台使用的是html作为视图层,不能够直接从servlet相关的域对象获取值,只能通过ajax获......
  • 前端性能精进之优化方法论(二)——分析
    在上一节中曾提到过两种性能监控:SYN和RUM,那么对应的也有两种分析:数据分析和实验室分析。数据分析会通过采集上来的性能信息来剖析和定位可能存在的各种问题。......
  • 这6种性能优化,让你的程序飞起来!
    软件设计开发某种意义上是"取"与"舍"的艺术。关于性能方面,就像建筑设计成抗震9度需要额外的成本一样,高性能软件系统也意味着更高的实现成本,有时候与其他质量属性甚至会冲......
  • vue3异步组件(defineAsyncComponent、Suspense的使用)
    异步组件按需引入,用到的时候再加载,异步组件的引入需要用​​defineAsyncComponent​​进行注册。​​defineAsyncComponent​​工厂函数需要返回一个Promise对象,接收对象类......