[TOC]
懒加载
懒加载是一种网页性能优化的常见方式,它能极大的提升用户体验。到今天,一个网站里面可能会有几百张甚至几千张上万张图片和其他资源,如果每次进入页面都需要请求页面上的所有的资源,会较大的影响用户体验,对用户的带宽也是一种极大的损耗。
所以,懒加载的意义即是,当页面未滚动到相应区域,该区域内的资源(网络请求)不会被加载。反之,当页面滚动到相应区域,相关资源的请求才会被发起。
之前,图片的懒加载通常都是使用 JavaScript 方案进行:
先给页面的图片设置一个占位符,不要把图片真实地址放在src中,真正的路径存储在一个属性中,要用的时候再取出来,页面加载完成后,判断图片是否在用户视野,如果在,就把值取出放入src属性;
如果 offsetTop - scrollTop < clientHeight,说明图片出现在可视区域
而今天,我们在懒加载实现上,有了更多不一样的选择。下面会介绍两种方式并进行测验:
- content-visibility: auto
- loading=”lazy” 和 decoding=”async”
1 使用 content-visibility 实现延迟渲染
1.1 What
content-visibility: auto
其控制一个元素,直到需要它的时候才会渲染
利用 content-visibility ,我们可以实现如果该元素当前不在屏幕上,则不会渲染其后代元素。
1.2 Demo
如果有如下页面结构,我使用 items 数组存放15个照片,都是通过网络请求拿到的,模板中进行渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| <div v-for="item in items" :key="item"> <div class="container-box"> <p>-------------{{ item.id }}-------------</p> <img :src="item.src" alt=""/> </div> </div> <script> export default { data() { return { items: [{ id: 0, src: "http://b.hiphotos.baidu.com/image/pic/item/9d82d158ccbf6c81b94575cfb93eb13533fa40a2.jpg" },{ id: 1, src: "https://gtd.alicdn.com/sns_logo/i3/TB1wnBTKFXXXXcQXXXXSutbFXXX.jpg_240x240xz.jpg" }, { id: 2, src: "//www.baidu.com/img/flexible/logo/pc/result.png" }, { id: 3, src: "http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg" }, { id: 4, src: "http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg" }, { id: 5, src: "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg" }, { id: 6, src: "http://g.hiphotos.baidu.com/image/pic/item/55e736d12f2eb938d5277fd5d0628535e5dd6f4a.jpg" }, { id: 7, src: "http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg" }, { id: 8, src: "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg" }, { id: 9, src: "http://e.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c1badd5a685d6277f9e2ff81e.jpg" }, { id: 10, src: "http://g.hiphotos.baidu.com/image/pic/item/0d338744ebf81a4c87a3add4d52a6059252da61e.jpg" }, { id: 11, src: "http://a.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee5080c8142ff5e0fe99257e19.jpg" }, { id: 12, src: "http://f.hiphotos.baidu.com/image/pic/item/4034970a304e251f503521f5a586c9177e3e53f9.jpg" }, { id: 13, src: "http://b.hiphotos.baidu.com/image/pic/item/279759ee3d6d55fbb3586c0168224f4a20a4dd7e.jpg" }, { id: 14, src: "http://a.hiphotos.baidu.com/image/pic/item/e824b899a9014c087eb617650e7b02087af4f464.jpg" }] } } } </script>
|
页面看起来是这样的:
只需要给需要延迟(实时)渲染的元素,设置简单的 CSS 样式:
1 2 3
| .container-box { content-visibility: auto; }
|
就可以实现延迟加载了
1.3 可访问性
全文检索是一个重要的功能
如果说可视区外的内容未被渲染,那是否会影响用户进行全文检索呢?
在页面初始化后,我全局 ctrl + F
,搜索 7 ,全局查找的时候,可以找到当前未被渲染的元素内的内容
因此,即便存在设置了 content-visibility: auto
的未被渲染的元素,但是它并不会影响全局的搜索功能。
1.4 But
文章还没到一半,当然不会就这么结束了
因为有2个缺点:
- 滚动条在向下滚动在不断的抖动:这是由于下面不在可视区域内的内容,一开始是没有被渲染的,在每次滚动的过程中,才逐渐渲染导致滚动条抽搐,用户体验挺差的
- 初始化时请求全部发送了:即便当前页面可视区域外的内容未被渲染,但是图片资源的 HTTP/HTTPS 请求,依然会在页面一开始被触发
1.3.1 利用 contain-intrinsic-size 解决滚动条抖动问题
当我们使用 content-visibility: auto
,那么可视区外的元素高度通常就为 0,当滚动条移动,元素展示,所以会出现抖动抽搐。
如果能准确知道设置了元素在渲染状态下的高度,就可以避免抖动现象了。
因此我为元素加上 contain-intrinsic-size 属性:
1 2 3 4
| .container-box { contain-intrinsic-size: 320px; content-visibility: auto; }
|
效果:
所以,content-visibility: auto 无法完全替代图片懒加载,设置了 content-visibility: auto 的确实现了延迟渲染,但是其中的静态资源仍旧会在页面初始化的时候被全部加载。因此,它更适合于虚拟列表渲染的情景。
2 懒加载+异步解码
2.1 懒加载 loading=”lazy”
HTML5 新增了一个 loading 属性。此属性可以添加到 < img > 元素中,也可以添加到 < iframe > 元素中。
属性的值为 loading=”lazy” 会告诉浏览器,如果图像位于可视区时,则立即加载图像,并在用户滚动到它们附近时获取其他图像。
loading=”lazy” 到(2023-07-08)的兼容性,还是非常不错的:
也可以通过以下方法检测浏览器是否支持 loading="lazy"
1
| var isSupportLoading = 'loading' in document.createElement('img');
|
2.2 异步解码 decoding=”async”
HTMLImageElement
接口的 decoding
属性用于告诉浏览器使用何种方式解析图像数据。
- sync: 同步解码图像,这会阻止主线程,直到图像完全解码。
- async: 异步解码图像,不会阻止主线程。
- auto: 默认模式,表示不偏好解码模式。由浏览器决定哪种方式更适合用户。
浏览器在进行图片渲染展示的过程中,是需要对图片文件进行解码的,这一个过程快慢与图片格式有关。
而如果我们不希望图片的渲染解码影响页面的其他内容的展示,可以使用 async 选项
这样,浏览器便会异步解码图像,加快显示其他内容,优化页面渲染性能。
同样的,我们来看看到(2023-07-08),decoding=”async” 的兼容性,整体还是非常不错的,作为渐进增强方案使用,是非常好的选择。
2.3 Demo
和上面的Demo差不多,准备14张图片的页面,每个图片url大小不一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <template> <div class="container"> <!-- loading="lazy" 懒加载 --> <div v-for="item in items" :key="item"> <div class="container-box2"> <p>-------------{{ item.id }}-------------</p> <img :src="item.src" alt="" loading="lazy" decoding="async"/> </div> </div> </div> </template> <script> export default { data() { return { items: [{ id: 0, src: "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg" },{ id: 1, src: "https://gtd.alicdn.com/sns_logo/i3/TB1wnBTKFXXXXcQXXXXSutbFXXX.jpg_240x240xz.jpg" }, { id: 2, src: "//www.baidu.com/img/flexible/logo/pc/result.png" }, { id: 3, src: "http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg" }, { id: 4, src: "http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg" }, { id: 5, src: "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg" }, { id: 6, src: "http://g.hiphotos.baidu.com/image/pic/item/55e736d12f2eb938d5277fd5d0628535e5dd6f4a.jpg" }, { id: 7, src: "http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg" }, { id: 8, src: "http://b.hiphotos.baidu.com/image/pic/item/9d82d158ccbf6c81b94575cfb93eb13533fa40a2.jpg" }, { id: 9, src: "http://e.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c1badd5a685d6277f9e2ff81e.jpg" }, { id: 10, src: "http://g.hiphotos.baidu.com/image/pic/item/0d338744ebf81a4c87a3add4d52a6059252da61e.jpg" }, { id: 11, src: "http://a.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee5080c8142ff5e0fe99257e19.jpg" }, { id: 12, src: "http://f.hiphotos.baidu.com/image/pic/item/4034970a304e251f503521f5a586c9177e3e53f9.jpg" }, { id: 13, src: "http://b.hiphotos.baidu.com/image/pic/item/279759ee3d6d55fbb3586c0168224f4a20a4dd7e.jpg" }, { id: 14, src: "http://a.hiphotos.baidu.com/image/pic/item/e824b899a9014c087eb617650e7b02087af4f464.jpg" }] } } } </script>
|
由于是纯图片页面,需要给给图片设置默认宽高。
否则初次刷新时,元素的宽高都是 0,会导致所有元素都在可视区内。
1 2 3 4 5 6 7 8
| <style> img { margin: 8px; width: 202px; height: 166px; object-fit: cover; } </style>
|
效果:
可以看到,初次加载时,只会请求当前页面的资源,随着可视区域变化,再进行资源请求
2.4 性能测试
为了验证性能,在上面的Demo基础上,进行对比测试。
首先是没有添加懒加载的元素
1
| <img :src="item.src" alt=""/>
|
可以看到,初次页面加载时会请求所有资源,页面 Load 事件完成的时间竟然有 14.96 s
现在我们为他加上 loading=”lazy” 与 decoding=async”
1
| <img :src="item.src" alt="" loading="lazy" decoding="async" />
|
首次进入页面仅请求显示在屏幕上的图片,页面 Load 事件完成的时间为 597 ms
当页面滚动时,显示的图片才会继续发送请求
3 总结一下
- content-visibility: auto + contain-intrinsic-size 可以实现延迟渲染,并且不会影响全局的搜索功能
- 但是初始化时,资源会全部加载,因此不太适合做图片懒加载,更适合于虚拟列表场景
- loading=”lazy” + decoding=”async” 可以实现懒加载+异步解码,浏览器只会加载可视区的资源