懒加载实现思路
(本文以vue为例,其他框架类同,学习记录)
图片懒加载使用场景
在我们制作的移动端应用中(包括WEB,app,小程序),时常会出现一种长列表的图文信息
而在我们写完成了之后,就会考虑如何去优化这个列表
在这个时候,我脑子里第一个想到的就是图片懒加载
因为在列表渲染过程中会发出很多个图片请求,尽管我们使用起来可能没什么感觉,但是实现一个懒加载可能会少发出很多的图片请求,致使服务器的压力变小
在工作中,我们总是会拿到很多的数据来渲染列表,通常我们的写法会是这样的
<template>
<div>
<div v-for="item in list" :key="item.id">
<div class="name">{{item.name}}</div>
<div class="img">
<img :src="item.img" />
</div>
</div>
</div>
</template>
而这种写法会使得只要list的数据出现了,同时也会发起多个图片请求
为了规避同时发起如此多的图片请求,所以才有了懒加载的出现
懒加载实现思路
在用户的滚动条滚动到该元素的位置时,我们才开始加载图片
所以,我们知道了,该使用监听滚动条的事件,来判断是否加载图片
于是我们知道了如下写法:
<script type="text/javascript">
exprot defalut {
data() {
return {
scroll: ''
}
},
methods: {
menu() {
this.scroll = document.documentElement.scrollTop || document.body.scrollTop;
console.log(this.scroll)
}
},
mounted() {
window.addEventListener('scroll', this.menu)
},
}
</script>
那么,现在我们实现了监听滚动事件,如何判断什么时候加载图片呢
毫无疑问,我们来判断当前img元素是否出现在视野当中,也就是对屏幕底部的距离如果大于0就开始加载图片
于是,我们写成这样
<template>
<div>
<div v-for="item in list" :key="item.id">
<div class="name">{{item.name}}</div>
<div>
<!-- 通过每个item的show来判断图片的路径是否正确达到控制图片加载的效果 -->
<img :src="item.show?item.img:''" />
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
list: [
{
id:1,
img:'../1.png',
name:'图片1',
show:false
},
{
id:2,
img:'../2.png',
name:'图片2',
show:false
}
]
}
},
methods: {
scroll() {
// 图片
let list = document.querySelectorAll(".img")
// 判断图片是否已经被加载,若未被加载,则进入判断
list.forEach((item,index)=>{
if(!this.list[index].show){
// 找出当前图片相对于视口的位置
let y = item.getBoundingClientRect().y
if (y > 0) {
// 更加当前item的show属性刷新视图,设置为true,则图片路径为正确路径开始发起图片请求
this.list[index].show = true
}
}
})
},
}
mounted() {
window.addEventListener('scroll', this.scroll)
},
}
</script>
到这里为止,我们发现,图片懒加载其实已经成功了,但是由于不停地监听滚动事件,所以我们的性能提升并没有我们想象之中的那么明显
幸运的是,浏览器给我们提供了IntersectionObserver 对象,用于推断某些节点是否可以被用户看见
于是,我们的代码变成了以下写法
tempalte部分
<template>
<div>
<div v-for="item in list" :key="item.id">
<div class="name">{{item.name}}</div>
<div>
<!-- 通过data-src属性填入实际图片路径阻止img加载真实图片,用加载图片代替 --!>
<img :data-src="item.img" src="../loading-img.png" />
</div>
</div>
</div>
</template>
js部分
mounted() {
//页面加载完成后,讲所有img挂载上IntersectionObserver
const imgs = document.querySelectorAll('img')
const io = new IntersectionObserver(entries => {
//多张图片时,我们需要用forEach来给循环
entries.forEach(entry => {
//判断每张图片是否出现在屏幕中
if (entry && entry.isIntersecting) {
//将元素上的data-src复制给元素的路径,开启图片加载
entry.target.src = entry.target.dataset.src
//同时停止观察该元素
io.unobserve(entry.target)
}
})
})
imgs.forEach(item => {
io.observe(item)
})
},
到目前为止,我们就以我目前知道最优的方法实现了一个vue的懒加载,其实其他框架也是同类逻辑,值得一提的是,微信内置的浏览器和小程序端也支持IntersectionObserver对象
小弟技术很菜,有讲的不对的轻喷
哈哈,欢迎各位大佬给出建议