方案一:视窗高度与图片元素距内容顶部判定+防抖优化实现
1.封装防抖
export default (func: ()=>void, delay: number) => { // 防抖 // 返回一个函数,该函数在delay时间内只执行一次 let timer: number | null = null; return () => { if (timer) { clearTimeout(timer); } timer = setTimeout(() => { func() }, delay); }; }
2.组件中应用
<template> <div class="lazyDiv"> <img v-for="item in imagesList" :key="item._id" :data-src="item.src" src="../../public/favicon.ico" alt="" class="lazyload"> </div> </template> <script setup> import {onMounted, ref} from 'vue' import axios from 'axios' import useDebounce from '@/hooks/useDebounce'; axios.defaults.baseURL = 'http://localhost:3000' let imagesList = ref([]) let getImages = async()=>{ let {data:{code,imageList}} = await axios.get('/getImages') if(code === 200){ imagesList.value = imageList console.log(imagesList.value); } } //获取所有img标签 const images = document.getElementsByTagName('img') //方案一:依据相应图片元素距离页面展示内容顶部距离(image.getBoundingClientRect().top)是否大于浏览器当前内容区窗口高(window.innerHeight)判定 let lazyload = ()=>{ console.log('lazyload触发'); [...images].forEach(image=>{ //下行获取目标图片image元素距离页面展示内容顶部距离 const topDistance = image.getBoundingClientRect().top //图片距内容顶部高于视窗高度做判定 if(topDistance < window.innerHeight){ //成立更换img的src属性 const data_src = image.getAttribute('data-src') image.setAttribute('src', data_src) } }) } onMounted(()=>{ getImages() window.addEventListener('scroll', useDebounce(lazyload, 500)) window.onload = ()=>{ lazyload() } }) </script> <style lang="less"> .lazyDiv{ width: 100%; height: 100%; img{ display: block; margin: 15px; width: 180px; height: 150px } } </style>
方案二:浏览器内置的IntersectionObserver
原理:
通过当图片与浏览器视口产生交叉区域,执行事件,并可以在事件执行之后清除监听(即成功加载图片后不会再触发)
组件中应用
<template> <div class="lazyDiv"> <img v-for="item in imagesList" :key="item._id" :data-src="item.src" src="../../public/favicon.ico" alt="" class="lazyload"> </div> </template> <script setup> import {onMounted, ref} from 'vue' import axios from 'axios' axios.defaults.baseURL = 'http://localhost:3000' let imagesList = ref([]) let getImages = async()=>{ let {data:{code,imageList}} = await axios.get('/getImages') if(code === 200){ imagesList.value = imageList } } //获取所有img标签 const images = document.getElementsByTagName('img') //方案二:当图片与浏览器视口产生交叉区域,执行事件,并可以在事件执行之后清除监听(即成功加载图片后不会再触发) const lazyLoad2 = ()=>{ //先执行同步代码逐个事件监听,所以这里callback才可以遍历正常 const callback = (entries)=>{ entries.forEach(entry=>{ // console.log('entry===',entry); if(entry.isIntersecting){ //如果产生交叉区域 const data_src = entry.target.getAttribute('data-src') //获取相应img标签属性,并做src属性赋值 entry.target.setAttribute('src', data_src) console.log('执行了一次监听'); observer.unobserve(entry.target) //执行监听后移除监听 } }) } const observer = new IntersectionObserver(callback); //创建一个观察者对象 [...images].forEach(image=>{ observer.observe(image) //遍历并监视每张图片 }) } onMounted(()=>{ getImages() window.onload = ()=>{ lazyLoad2() } }) </script> <style lang="less"> .lazyDiv{ width: 100%; height: 100%; img{ display: block; margin: 15px; width: 180px; height: 150px } } </style>