20.图片懒加载的3种方法
方案: ①首屏加载时候img标签src赋为空值,这样就不会去渲染看不见的图片而浪费时间; ②当用户滑动到图片的可视区域后,替换src的路径,改为正式路径,则开始渲染图片;
好处:一是首屏加载快,二是节省流量。在图片没有到达可视区域的时候不会加载,因为不可能每一个用户会把页面从上到下滚动完。
方式一:传统方法通过监听滚动条来实现
<template>
<div>
<img v-for="(item,i) in lazyImgs" style="width: 180px;height:240px;margin-top:40px;display: inline-block;" :data-src="item" :key="i" src="" alt="">
</div>
</template>
<script>
export default{
data(){
return{
lazyImgs:[]
}
},
async mounted() {
window.addEventListener("scroll", (e) => {
// 这里做一个 节流 操作
if (this.timer) return;
this.timer = setTimeout(() => {
this.query("img[data-src]").forEach((img) => {
const rect = img.getBoundingClientRect();
if (rect.top < window.innerHeight) {
img.src = img.dataset.src;
// 我们是通过img[data-src]查找所有img标签的,渲染后就删除data-src可减少forEach循环的计算成本
img.removeAttribute("data-src");
}
});
clearTimeout(this.timer);
// 这里一定要把定时器置为 null
this.timer = null
}, 300);
});
},
methods: {
query(selector) {
return Array.from(document.querySelectorAll(selector));
},
}
}
</script>
方式二:利用监听器实现(封装组件)
注意:用到的api:IntersectionObserver。就是一个监听器,学名叫交叉观测器,可以监听任何元素,当元素进入可视区域内,便会触发回调函数
弊端:IntersectionObserver方法可能没有兼容全浏览器,如果要实现兼容全浏览器,需要引入对应的插件实现
步骤一:新建一个文件lazyImg.vue
<template>
<div class="img-box" v-lazy="vm" :data-src="src">
<slot v-if="slotShow"></slot>
<slot name="err" v-if="errFlag"></slot>
</div>
</template>
<script>
export default {
props: {src: {type: String,default: ""}},
data() {return {slotShow: true,errFlag: false,vm: null,}},
created() {this.vm = this;},
directives: {
lazy: {
inserted(el, { value }) {
const imgSrc = el.dataset.src;
const observer = new IntersectionObserver(([{ isIntersecting }]) => {
if (isIntersecting) {
// 动态创建 img 添加到父元素内(若不动态加载,图片会从上到下依次加载,上面加载完成再显示下面的,在滚动的时候就会感觉有卡顿感)
let img = document.createElement("img");
img.src = imgSrc;
img.style.width = "100%";
img.style.height = "100%";
// 添加图片加载完成事件:加载完成,让加载前的样式隐藏
img.onload = function () {value.slotShow = false;};
(img.onerror = function () {
value.errFlag = true; // 加载失败显示的样式
value.slotShow = false; //隐藏加载前的样式
}),
el.appendChild(img);
observer.unobserve(el);
}});
observer.observe(el)}}
},
methods: {
loadImg() {this.slotShow = false},
error(e) {
if(!e.srcElement.dataset.flag||!this.errFlag) return false
// 这里我们就不给设置失败后的图片了,留给使用者自行设置样式
// e.srcElement.src = this.errorImg
this.errFlag = false
this.slotShow = false}}
};
</script>
<style lang="less" scoped>
.img-box {display: flex;position: relative;overflow: hidden;}
img {width: 100%;height: 100%;object-fit: cover;}
</style>
步骤二:组件中使用
<template>
<div>
//图片懒加载最好设置图片高度,因为不管你是监听滚动条的方式,还是利用监听器api实现,都跟元素可视区域有关系,而高度就影响是否在可视区域内
<lazyImg :index="i" v-for="(item,i) in lazyImgs" :key="i" :src="item">
//图片加载之前默认在图片元素上方展示的样式
<div class="slot-txt">加载中</div>
<template #err>
//图片加载失败后在上面展示的样式
<div class="slot-txt">加载失败</div>
</template>
</lazyImg>
</div>
</template>
<script>
import lazyImg from './components/lazyImg'
export default {
components: {
lazyImg
},
data: {
return {
lazyImgs: ['@assets/imgs/img1.png','@assets/imgs/img2.png','@assets/imgs/img2.png']
}
}
}
</script>
<style lang="scss" scoped>
.slot-txt {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
background: #f9ccd4;
z-index: 100;
}
</style>
方式三:利用监听器实现(全局方法调用)
步骤一:main.js:设置全局自定义指令,命名为lazy
// 定义懒加载图片或者文件等,自定义指令 Vue.directive('lazy', (el, binding) => { let oldSrc = el.src //保存旧的src,方便后期渲染时候赋值src真实路径 el.src = "" //将渲染的src赋为空,则不会渲染图片出来 // 调用方法得到该elDOM元素是否处于可视区域 let observer = new IntersectionObserver(([{ isIntersecting }]) => { if (isIntersecting) { //回调是否处于可视区域,true or false el.src = oldSrc //如果处于可视区域额,将最开始保存的真实路径赋予DOM元素渲染 observer.unobserve(el) // 只需要监听一次即可,第二次滑动到可视区域时候不在监听 } }) observer.observe(el) // 调用方法 })
步骤二:组件中使用
<template>
<div>
//v-lazy
<div><img v-lazy src="@assets/imgs/img1.png" alt="img" /></div>
<div><img v-lazy src="@assets/imgs/img2.png" alt="img" /></div>
</div>
</template>
转载请注明原文链接:https://www.cnblogs.com/chenJieLing/

浙公网安备 33010602011771号