1、原理
借助IntersectionObserver这个类来判断当前图片列表中的某个图片是否进入可视区域,刚开始先给src设置一个图片作为初始值,这样图片没有出现在可视区域是,只需要加载一次。将真正的图片设置在data-src属性上,当出现在可视区域时,将data-src赋值给src。
IntersectionObserver接收两个参数,第一个参数是一个回调函数,回调函数接收一个entries,是一个需要监视的元素的数组,第二个参数是一个配置项,可以配置一些属性,详情见IntersectionObserver。
2、代码实现
创建文件useLazyImg.ts,编写代码如下
import { onMounted, Ref } from 'vue'
export const useLazyImg = (ref: Ref) => {
const observer = new IntersectionObserver(callback)
onMounted(() => {
// 监听传入的每一个对象
Object.keys(ref.value).forEach((e) => observer.observe(ref.value[e]))
})
}
function callback(entries: IntersectionObserverEntry[], observer: IntersectionObserver) {
entries.forEach(async enter => {
// 每一个目标元素的可见性变化
if (enter.intersectionRatio > 0) {
const img: Element = enter.target
const src = img.getAttribute('data-src')
img.setAttribute('src', src!)
observer.unobserve(img) // 加载完成后停止监听
}
})
}
3、使用
在文件中引入useLazyImg并且传入图片的ref
<template>
<div class="container">
<img
ref="imgRef" // 相同的ref会变成一个数组
v-for="(item,index) in imgList"
:key="index"
class="img-item"
src="https://img.zcool.cn/community/0196fa582abab6a84a0d304f899eaf.gif"
:data-src="item"
alt="">
</div>
</template>
<script setup lang="ts">
import '@/assets/index.less'
import { ref, reactive } from 'vue'
import { useLazyImg } from './hooks/useLazyImg'
const imgList = reactive([
'./src/assets/images/test.jpg',
'./src/assets/images/test.jpg',
'./src/assets/images/test.jpg',
'./src/assets/images/test.jpg',
'./src/assets/images/test.jpg',
'./src/assets/images/test.jpg',
'./src/assets/images/test.jpg',
'./src/assets/images/test.jpg',
'./src/assets/images/test.jpg',
'./src/assets/images/test.jpg',
])
const imgRef = ref<HTMLImageElement>()
useLazyImg(imgRef)
</script>
<style scoped>
.container{
width: 300px;
}
.img-item{
width: 300px;
height: 300px;
}
</style>