当用户向服务端请求一组图片数据后,还需对每个图片的src再次请求,当图片数量较多时,图片的响应和加载完成会消耗大量时间,影响用户体验。本文旨在通过vue插件的方式实现图片懒加载,原理是监听滚动元素的滚动事件,当图片将要出现在视口时再进行加载
定义图片监听构造函数
// vue-lazyload/lazy.js
class ReactiveListener {
constructor({el, src, options, elRender}){
this.el = el;
this.src = src;
this.options = options;
this.elRender = elRender;
this.state = {loading: false}
}
checkInview = () => {
let { top } = this.el.getBoundingClientRect();
return top < window.innerHeight * this.options.preLoad
}
loadImgAsync = (src, resolve, reject) => {
let image = new Image();
image.src = src;
image.onload = resolve;
image.onerror = reject
}
load = () => { // 3.2. 加载:开始渲染和渲染前 默认loading;异步加载成功时,渲染真实的图片;失败,传递失败态
this.elRender(this, 'loading');
this.loadImgAsync(this.src, () => {
this.state.loading = true
this.elRender(this, 'loaded');
}, () => {
this.elRender(this, 'error');
});
}
}
定义懒加载构造函数
// vue-lazyload/lazy.js
class LazyClass {
constructor(options) {
this.options = options;
this.listenerQuene = [];
this.bindHandler = false;
}
lazyLoadHandler = throttle(() => {
let catIn = false;
this.listenerQuene.forEach(listener => {
if (listener.state.loading) return; // 已经渲染过不再处理
catIn = listener.checkInview();
catIn && listener.load();
})
}, 300)
elRender = (listener, state) => { // 2.1.图片渲染函数
let {el, options, src} = listener;
let imgSrc = ''
switch (state) {
case 'loading':
imgSrc = options.loading
break;
case 'error':
imgSrc = options.error || ''
break;
default:
imgSrc = src
break;
}
el.setAttribute('src', imgSrc)
}
add = (el, bindings) => {
Vue.nextTick(() => { // 在bind时无法获取真实的dom,所以使用$nextTick
function scrollParent() {
let parent = el.parentNode;
while(parent) {
if (/scroll|auto/.test(getComputedStyle(parent)['overflow'])) {
return parent
}
parent = parent.parentNode;
}
return parent
}
let parent = scrollParent();
let src = bindings.value;
let listener = new ReactiveListener({
el,
src,
options: this.options,
elRender: this.elRender
});
this.listenerQuene.push(listener);
if(!this.bindHandler) {
this.bindHandler = true;
parent.addEventListener('scroll', this.lazyLoadHandler);
}
this.lazyLoadHandler(); // 默认执行一次,进行内容渲染
})
}
}
定义install方法
当通过Vue.use引入插件时,默认会调用插件的install方法;在install方法中,创建Lazy实例,定义v-lazy指令,当指令绑定在元素上时,执行实例的add方法
// 文件vue-lazyload/index.js
import Lazy from './lazy'
export default {
install(Vue, options){
const LazyClass = Lazy(Vue);
const lazy = new LazyClass(options);
Vue.directive('lazy', {
bind: lazy.add.bind(lazy)
})
}
}
引入VueLazyload插件
import Vuelazyload from './vue-lazyload'
Vue.use(Vuelazyload, {
preLoad: 1.3,
loading: loading,
})
使用
<li v-for="(img, index) in imgs" :key='index'>
<img v-lazy="img">
</li>
---------------------------------------------
data() {
return {
imgs:['https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg','https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3892521478,1695688217&fm=26&gp=0.jpg']
}
}