有些页面可能展示的是大量的图片,如果我们一次性加载所有图片就会浪费性能,影响用户体验,所以我们就会懒加载这些图片。即可视区域之外的图片不加载,随着页面的滚动,图片进入可视区域,则触发图片的加载显示。
优点:页面加载速度快,用户体验感更好且节省流量
<img data-src="xxx" src="xxx" width="180" height="180"/>
实现图片懒加载的多种方法的区别就在于判断元素是否进入视口的方式不同,当使用方法3和方法4时,如果发生滚动事件,会产生大量的循环和判断操作判断图片是否可视区里,因此需要用节流进行优化。
github地址
原理
vue-lazyload的核心原理是利用了IntersectionObserver
API,这是一个用于检测元素是否与视口相交的API,它可以高效地监听元素的可见性变化,并触发回调函数。
vue-lazyload在注册插件时,会创建一个全局的IntersectionObserver
实例,并设置一些选项,如阈值(threshold)、根元素(root)等。然后,在绑定v-lazy
指令时,会创建一个监听器(listener)对象,并将其添加到一个监听器队列(listenerQueue)中。每个监听器对象都包含了元素的相关信息,如状态(state)、图片地址(src)等。
接下来,vue-lazyload会遍历监听器队列,并调用IntersectionObserver
实例的observe
方法,将每个元素注册到观察者中。当元素与视口相交时,IntersectionObserver
实例会触发回调函数,并传入一个entries参数,表示所有被观察的元素的状态信息。vue-lazyload会根据entries中的isIntersecting
属性判断元素是否可见,如果是,则调用监听器对象的load方法,将元素的src或者style属性替换为真实的图片地址,并将该监听器对象从队列中移除。
基本用法
npm i vue-lazyload@1.2.3 -S
// 1.图片懒加载插件
import VueLazyload from 'vue-lazyload'
// 2.注册插件
Vue.use(VueLazyload, {
//参数配置 可不填
// 懒加载默认加载图片
loading: 'xxx.png',
// 加载失败后加载的图片
error: 'xxx.png',
preLoad: 1.3, // 预加载高度的比例
attempt: 3 // 尝试加载次数
})
vue-lazyload提供了一个自定义指令v-lazy,可以在img标签或者任何需要设置背景图片的标签上使用它。例如:
<!-- 懒加载img标签 -->
<img v-lazy="imgUrl" />
<!-- 懒加载背景图片 -->
<div v-lazy:background-image="bgUrl"></div>
v-lazy指令接收一个字符串类型的值,表示图片的地址。如果是背景图片,需要在指令后加上:background-image修饰符。
当页面滚动时,vue-lazyload会检测元素是否进入可视区域,如果是,则替换元素的src或者style属性,从而实现懒加载。
⚠️ 若图片为循环渲染、分页显示,则必须写key值,不然切换页面后页面视图不刷新
github地址
原理与使用方法与vue2版本插件相同,仅安装不同
npm i vue-lazyload-next -S
import VueLazyloadNext from 'vue-lazyload-next';
app.use(VueLazyloadNext, {
// 添加一些配置参数 可不填
// 懒加载默认加载图片
loading: 'xxx.png',
// 加载失败后加载的图片
error: 'xxx.png',
preLoad: 1.3, // 预加载高度的比例
attempt: 3 // 尝试加载次数
});
这个插件支持背景图;且图片不会反复加载【加载过的不会再加载】;
API
原理
Intersection Observer是是浏览器原生提供的构造函数,使用它能省到大量的循环和判断。这个构造函数的作用是它能够观察可视窗口与目标元素产生的交叉区域。简单来说就是当用它观察我们的图片时,当图片出现或者消失在可视窗口,它都能知道并且会执行一个特殊的回调函数,就可以利用这个回调函数实现需要的操作。
hook
vue3中选择用hook进行集成
useLazyload.ts文件
// 定义自定义指令
const defineDirective = (app: any) => {
app.directive('lazy', {
mounted(el: HTMLImageElement, bindings: any) {
// el表示使用指令的DOM元素
// 指令的功能:实现图片的懒加载
// 1、监听图片是否进入可视区
const observer = new IntersectionObserver(([{ isIntersecting }]) => {
// true;进入可视区域,false:未进入可视区域
if (isIntersecting) {
// 1、给图片的src属性赋值图片的地址
el.src = bindings.value;
// 2、取消图片的监听,默认是会一直监听的,如果不取消。就会一直执行
// eslint-disable-next-line spellcheck/spell-checker
observer.unobserve(el);
}
});
// 监听dom元素
observer.observe(el);
}
});
};
export default {
install(app: any) {
// 自定义指令
defineDirective(app);
}
};
基本用法
main.ts
import lazy from './hooks/useLazy';
app.use(lazy);
对需要进行懒加载的img标签,直接将:src替换为v-lazy
<img
class="image"
:key="data.rendered_result_image_url"
v-lazy="data.rendered_result_image_url"
/>
⚠️若图片为循环渲染、分页显示,则必须写key值,不然切换页面后页面视图不刷新
HTML新增 loading属性
基本用法
<img src="xxx.png" loading="lazy">
loading 属性支持 3 种属性值:
图片必须声明width和height,不然会看到布局发生移动
兼容性
不适用于背景图情况;
如何判断元素是否到达可视区域
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
,实现懒加载API
//获取所有img标签
const imgs = document.getElementsByTagName('img');
onMounted(() => {
//用于首屏加载
lazyLoad();
//添加滚动事件监听
document.addEventListener('scroll', throttle(lazyLoad, 500), true);
});
onUnMounted(() => {
document.removeEventListener('scroll', throttle(lazyLoad, 500), true);
});
// 节流
const throttle = (fn: { apply: (arg0: any, arg1: any[]) => void }, t: number) => {
let flag = true;
const interval = t || 500;
return function (this: any, ...args: any) {
if (flag) {
fn.apply(this, args);
flag = false;
setTimeout(() => {
flag = true;
}, interval);
}
};
};
const lazyLoad = () => {
const offsetHeight = window.innerHeight || document.documentElement.clientHeight;
Array.from(imgs).forEach(item => {
const oBounding = item.getBoundingClientRect(); //返回一个矩形对象,包含上下左右的偏移值
if (0 <= oBounding.top && oBounding.top <= offsetHeight) {
// //性能优化 进行判断 已经加载的不会再进行加载
if (item.getAttribute('alt') !== 'loaded') {
item.setAttribute('src', item.getAttribute('data-src') as string);
item.setAttribute('alt', 'loaded');
}
}
});
};
缺点