1、懒加载的概念
懒加载也叫做延迟加载、按需加载。指的是在长网页中延迟加载图片数据,是一种较好的网页性能优化的方式。
在比较长的网页或应用中,如果图片很多,所有的图片都被加载出来,而用户只能看到可视窗口的那一部分图片数据,这样就浪费了性能。如果使用图片的懒加载就可以解决以上问题。在滚动屏幕之前,可视化区域之外的图片不会进行加载,在滚动屏幕时才加载。这样使得网页的加载速度更快,减少了服务器的负载。
懒加载适用于图片较多,页面列表较长(长列表)的场景中。
2、懒加载的特点
- 减少无用资源的加载
使用懒加载明显减少了服务器的压力和流量,同时也减小了浏览器的负担。- 提升用户体验
同时加载较多图片,可能需要等待的时间较长会影响用户体验,使用懒加载可以大大的提高用户体验。- 防止加载过多图片而影响其他资源文件的加载
加载过多图片可能会影响网站应用的正常使用。
3、图片懒加载的方式
(1)img 标签的 loading 属性
loading 属性值【auto 、eager 、lazy 】
- auto 浏览器默认的懒加载策略,和不增加这个属性的表现一样
- eager 立即加载图像,不管它是否在视口之外(默认值);
- lazy 延迟加载图像,直到它和视口接近到一个计算得到的距离(由浏览器定义,chrome、firfox、edge、safari 都是出现则立即加载)。注意,img 元素需要指定宽高 lazy 才能生效。
<img src="image.png" loading="lazy" width={200} height={200} />
注:图片必须声明 width 和 height,不然会布局发生移动
优点:使用简单,兼容性也还可以。
缺点:图片加载完成前,会看到空白区域,用户体验不是很好。
(2)JS事件监听
监听 scroll、resize 等事件,当事件触发时,获取图片元素的位置信息、滚动高度及视口高度,计算出当前图片元素是否出现在视口内,如果出现了则加载图片。
- window.innerHeight 是浏览器可视区的高度;
- document.body.scrollTop || document.documentElement.scrollTop 是浏览器滚动的过的距离;
- imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距离);
- 内容达到显示区域: img.offsetTop < window.innerHeight + document.body.scrollTop;
- 到达可视区域后,imgs[i].src = imgs[i].getAttribute('data-src') as string;将 data-src 属性值赋值给 src,实现懒加载
<div class="container">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<div>
<script>
// 获取全部的图片元素
let imgs = document.querySelectorAll('img')
function lazyLoad () {
// 获取浏览器滚动的过的距离
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop
// 获取浏览器可视区的高度
var winHeight = window.innerHeight
for (let i = 0; i < imgs.length; i++) {
// 判断是否满足图片加载条件
if (imgs[i].offsetTop < scrollTop + winHeight) {
imgs[i].src = imgs[i].getAttribute('data-src')
}
}
}
window.onscroll = lazyLoad()
</script>
优点:所有浏览器都兼容,可以比较灵活,自定义触发加载的时机,可以结合图片加载预览提升用户体验。
缺点:需要通过事件触发,图片的位置需要手动计算,容易导致性能问题。
(3)lazyload 插件
v-lazy 指令接收一个字符串类型的值,表示图片的地址。如果是背景图片,需要在指令后加上 :background-image 修饰符(注意冒号)。
当页面滚动时,vue-lazyload 会检测元素是否进入可视区域,如果是,则替换元素的 src 或者 style 属性,从而实现懒加载。
注意:若图片为循环渲染、分页显示,则必须写 key 值,不然切换页面后页面视图不刷新
vue-lazyload 提供了一个自定义指令 v-lazy
,可以在img标签或者任何需要设置背景图片的标签上使用它
<!-- 懒加载img标签 -->
<img v-lazy="imgUrl" />
<!-- 懒加载背景图片 -->
<div v-lazy:background-image="bgUrl"></div>
安装命令
- vue2:npm i vue-lazyload@1.2.3 -S
- vue3:npm i vue-lazyload-next -S
导入注册
// vue 2 导入
import VueLazyload from 'vue-lazyload'
// vue 2 注册
Vue.use(VueLazyload, {
//参数配置 可不填
// 懒加载默认加载图片
loading: 'xxx.png',
// 加载失败后加载的图片
error: 'xxx.png',
preLoad: 1.3, // 预加载高度的比例
attempt: 3 // 尝试加载次数
})
// vue 3 导入
import VueLazyloadNext from 'vue-lazyload-next';
// vue 3 注册
app.use(VueLazyloadNext, {
// 添加一些配置参数 可不填
// 懒加载默认加载图片
loading: 'xxx.png',
// 加载失败后加载的图片
error: 'xxx.png',
preLoad: 1.3, // 预加载高度的比例
attempt: 3 // 尝试加载次数
});
(4)Intersection Observer API
- IntersectionObserver 是浏览器原生提供的构造函数,使用它能省到大量的循环和判断。这个构造函数的作用是它能够观察可视窗口与目标元素产生的交叉区域。简单来说就是当用它观察我们的图片时,当图片出现或者消失在可视窗口,它都能知道并且会执行一个特殊的回调函数,就可以利用这个回调函数实现需要的操作。
- 解释补充:目标元素位置是通过 getBoundingClientRect() 方法确定的,所以无论是图片在正常文档流中还是脱离文档流,或者使用了 transform 改变渲染位置,最终都会根据元素相对于视口的实际渲染位置进行处理(可以理解为和视觉上是统一的,看到了就会触发)
useEffect(() => {
const container = ref.current;
const imgElements =
container?.querySelectorAll<HTMLImageElement>('.lazy');
const imgObserver = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target as HTMLImageElement;
img.src = img.dataset.src || '';
img.classList.remove('lazy');
imgObserver.unobserve(img);
}
});
});
imgElements?.forEach(function (img) {
imgObserver.observe(img);
});
}, []);
- 创建观察器后,使用观察器对所有懒加载 img 元素进行观察,创建观察器时传入的回调函数会在每个 img 元素进入视口时调用。
- 回调函数的 entries 参数是状态发生变化的元素对象,entry 对象的 isIntersecting 为 true 则说明元素进入了视口。
- 上面代码中,当元素进入视口后,修改 img 元素的 src 来加载图片,并取消对当前元素的观察。
优点:使用简单,不依赖事件监听,也不需要手动计算元素位置,性能更好。
本文引用了以下博主的优秀文章