在vue中实现图片懒加载,并引入promise判断加载状态和优化
为什么需要懒加载?
答: 当你的一个网页,图片有几十张,上百张时,同时的加载,有可能会导致网页出现很长一段时间的空白,而且用户并不一定会浏览全部的图片,全部加载完,也会浪费用户的浏览,这样的体验是很不好的.
效果如图:
那么如何在vue项目中实现懒加载呢?
1.懒加载的原理
一张图片就是一个img标签,浏览器是根据src的属性发起图片请求的.那么我们实现的依据就是当前图片出现在可视屏幕上,就把图片的地址赋值(或替换)给img的src属性.
2.懒加载的实现步骤
2.1 默认loading图片
2.2 判断哪些需要加载(难点)
2.3 预缓存在浏览器
2.4 替换真图片
3.代码实现
3.1 默认loading图片
loading找一些1-2K的图片,越小越好,放置在本地assets目录即可
<template>
<div class="img-loading-container" ref="imgBox">
<img
v-for="(item,index) in 5"
:key="index"
src="@/assets/loading.gif"
alt=""
>
</div>
</template>
3.2 判断哪些需要加载
当图片进入可视区域时,应用网友的一张图
如上图所示, 当图片(指当前需要加载的图片)距离顶部的距离小于可视区域h和滚动区域高度s之和时说明图片进入可视化区域了,即top-height < s+h时,图片在可视区。
介绍用到的api函数:
网页被卷去的高: document.body.scrollTop;
可视区域高度: window.innerHeight
当前对象到浏览器顶部的距离: domObject.offsetTop
具体代码:
<script>
export default {
data () {
return {
imgList: [
{
url: 'https://.../distribution-web/upLoad/img/ceshi/1.jpg'
},
{
url: 'https://.../distribution-web/upLoad/img/ceshi/2.jpg'
},
{
url: 'https://.../distribution-web/upLoad/img/ceshi/3.jpg'
},
{
url: 'https://.../distribution-web/upLoad/img/ceshi/4.jp' // 这是故意写错的
},
{
url: 'https://.../distribution-web/upLoad/img/ceshi/5.jpg'
}
],
present: {
old: -1,
new: 0
}
}
},
mounted () {
// 进入当前页面就加载
setTimeout(this.getImgList, 300)
// 监听滚动事件
window.addEventListener('scroll', this.getImgList)
},
methods: {
getImageCache (imgNodes, id) {
return new Promise((resolve, reject) => {
// 创建一个image对象
var temp = new Image();
// 预先把加载的图片加入内存中(隐形加载),相当于给浏览器缓存了一张图片
temp.src = this.imgList[id].url
// 成功的状态
temp.onload = () => {
imgNodes[id].src = temp.src
resolve('第' + (id + 1) + '张图片加载成功') };
// 失败的状态
temp.onerror = () => reject('第' + (id + 1) + '张图片加载失败了');
})
},
getImgList () {
// 获取所有img的DOM对象
const imgNodes = this.$refs.imgBox.childNodes
// 可视区域高度
const h = window.innerHeight;
// 滚动区域高度
const s = document.documentElement.scrollTop || document.body.scrollTop;
for (let i = this.present.new; i < imgNodes.length; i++) {
// 可视区域和滚动区域之和时懒加载大于图片距离顶部的距离
if ((h + s) > imgNodes[i].offsetTop) {
// 已加载的最大下标赋值给present做记录,防止再循环
this.present.new = i
// 当加载的最大下标值和上一次的下标值相等时,防止再请求
if (this.present.old !== this.present.new) {
this.getImageCache(imgNodes, i).then((success) => {
console.log(success)
}).catch((error) => {
console.log(error)
imgNodes[i].src = require('@/assets/error.jpg')
})
// 把当前最大下标值赋值给上一次最大下标值
this.present.old = this.present.new
}
}
}
}
}
}
</script>
你们可能会遇到的问题(因为我遇到过…)
1.当图片的height的数值设置为auto时,你会发现domObject.offsetTop(当前对象到浏览器顶部的距离)的值很小,然后你用domObject.offsetHeight(获取dom对象的高度)发现值为0!,解决方法有加个一次性计时器,还有就是放置在temp.onload中
2. 当你运用我的代码,打开浏览器的调试network时,每一张图片居然会加载两次或者很多次! 你再仔细看看你加载图片的Request Headers的两个属性,分别是cache-control和pragma。 他们的值都为no-cache(禁止缓存),这就是你浏览器的设置问题了,你在Disable cache打了勾,去掉就行了,如图
**最后:**本文章参考了tomorrownan的博客,后根据自己的思路,改造成vue的写法和加入promise,及把遇到的问题(已解决)分享给大家。
虚妄狼: 没有list组件吗?
2401_83828519: 打开ADB要输入验证吗,验证码是啥
巧宝er: 有用,原来是错在这里。一直在找问题。请问为什么要这么写呢?
竹月先生: vue2项目也是这个插件吗
Mei_Tou_Nao_: 太棒啦,我愁了好久终于解决了