方式1:监听元素scroll
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
img {
display: block;
height: 50vh;
width: 50vw;
border: 1px solid #000;
}
#app {
height: 100vh;
overflow: auto
}
</style>
</head>
<script src="https://unpkg.com/vue@3.0.5/dist/vue.global.js"></script>
<body>
<div id="app">
<button @click="arrChange">去掉</button>
<img v-for="i in [1,2,3,4,5,6]" :key="i" :alt="i" v-lazy="imgUrl" alt="">
</div>
<script>
var vue3Composition = {
setup () {
var imgUrl = 'https://sngedu-punch-1251502357.cos.ap-guangzhou.myqcloud.com/agency_picurl/QFzQYCgCrxnEM2K8pFvSckZV2zcSPicxDYYIfgqvqd3IOlbBggQialjAV0mcfXYVeo'
return {
imgUrl
}
},
}
let dom = document.getElementById('app')
var ObserverList = []
var vm = Vue.createApp(vue3Composition)
var scrollFun = (() => {
// 初始化已经显示图片
var initUrl = num => {
ObserverList.forEach(({el, value}, index) => {
if (el.offsetTop - 150 < num) {
// 元素高度小于 num 加载
el.src = value
ObserverList.splice(index, 1)
}
})
}
return () => {
initUrl(dom.scrollTop + document.body.clientHeight)
dom.addEventListener('scroll', () => {
initUrl(dom.scrollTop + document.body.clientHeight)
})
}
})()
vm.directive('lazy', {
mounted(el, {value}) {
// console.log(el.offsetTop)
ObserverList.push({el ,value})
scrollFun()
}
})
vm.mount('#app')
</script>
</body>
</html>
方式2:利用IntersectionObserver监听元素出现在视口中
// 图片懒加载自定义指令 | vue3版本
import baseImg from '@/assets/logo.png'
let timer = null
// 创建监听器
let observer = new IntersectionObserver((entries) => {
// entries是所有被监听对象的集合
entries.forEach((entry) => {
if (entry.isIntersecting || entry.intersectionRatio > 0) {
// 当被监听元素到临界值且未加载图片时触发。
!entry.target.isLoaded && showImage(entry.target, entry.target.data_src)
}
})
})
function showImage(el, imgSrc) {
// setTimeout查看效果
// setTimeout(() => {
console.log('1')
const img = new Image()
img.src = imgSrc
img.onload = () => {
el.src = imgSrc
el.isLoaded = true
}
// }, 1000)
}
export default {
mounted (el, binding, vnode) {
clearTimeout(timer)
// 初始化时展示默认图片
el.src = baseImg
// 将需要加载的图片地址绑定在dom上
el.data_src = binding.value
observer.observe(el)
// 防抖,这里在组件卸载的时候停止监听
const vm = vnode.context
// console.log(vm)
timer = setTimeout(() => {
vm.$on('hook:beforeDestroy', () => {
observer.disconnect()
})
}, 20)
}
}
方式3 插件vueuse
效果:图片进入到视口才加载(不理想)
参考代码(vue3)
// main.js
import directives from './directives';
app.use(directives)
app.mount('#app')
// directives.js
import { useIntersectionObserver } from '@vueuse/core'
export default {
install (app) {
app.directive('img-lazy', {
mounted(el , binding){
//el指令绑定到的元素。这可以用于直接操作 DOM
//binding: binding.value 指令等于后面绑定表达式的值 图片url
const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => {
if (isIntersecting) {
// console.log(true);
el.src = binding.value;
// 停止监听
stop();
}
});
}
})
}
}
方式4 插件 vue-lazyload
效果:图片进入到视口前加载,效果较好,适合滑动加载,
缺点:会造成1张图片2次加载,暂时没有解决办法
参考代码(vue3)
// main.js
import VueLazyload from 'vue-lazyload'
import loadImage from '@/assets/images/temp.png'
app.use(VueLazyload, {
preLoad: 1.3, //预加载的宽高比
loading: loadImage, //图片加载状态下显示的图片
// error: errorimage, //图片加载失败时显示的图片
attempt: 1, // 加载错误后最大尝试次数
})