赞
踩
因为列表页的图片太多,导致性能降低,所以决定用懒加载优化一下。所谓懒加载,就是图片没有显示的时候用占位图,等到显示的时候,将展位图改成真正要展示的图片。
提到懒加载,第一个想到的当然是onPageScroll啦。
首先,选一张占位图,并且在data中声明一下图片的高度和一个跟列表长度一样的false数组(这边为什么全部是false?因为要让图片全部先用占位图占着,等到显示的时候改为true)。
data: {
damoHeight :'110',
arry:[false,false,false,false,false,false]
},
然后在onload中处理首屏要转为true的图片,并进行渲染。
onLoad: function () { let self = this; swan.getSystemInfo({ success: res => { let windowHeight = res.windowHeight;//获取屏幕的高度 } }); //num是首屏要渲染的数量 let num = Math.ceil(windowHeight / self.data.damoHeight);// self.data.damoHeight为data中图片的高度 self.data.num = num; for(let i = 0; i < num; i++){ self.data.arry[i] = true;//将要渲染的几张图片在数组里改为true }; //将得到的新数组渲染到页面 self.setData({ arry:self.data.arry }); }
接着在onPageScroll方法中监听scrollTop(滚动顶端的值即滚动的像素),计算有几张图片的状态要改为true,从而实现想要的效果。
onPageScroll: function (res){
let self = this;
let str = parseInt(res.scrollTop / self.data.damoHeight);// self.data.damoHeight为data中图片的高度
that.data.arry[self.data.num+str] = true;//这边的序号要加上之前已经显示的数量
//将得到的新数组渲染到页面
that.setData({
arry:that.data.arry
})
},
界面布局
<image src="{{arry[index] ? catagory.img : '/images/loading.gif'}}" ></image>
效果是实现了,但是页面列表的数据会根据不同条件的选择,列表的数量是不固定的,所以我们没有办法先写死false数组。然后我在下拉的过程中,发现数组的长度会自动往后加。本以为大功告成了,但是有一个bug,就是当我下拉的速度非常快的时候,会有一些位置没有补true,例:
查了资料才知道,因为onPageScroll方法是下拉监听函数,在下拉的过程中一直在执行,在这里面setData是非常耗性能的,所以把它做了延时。我们懒加载的目的就是优化性能,所以怎么能用耗性能的方法呢,我又找到了下面这种方法。
什么是IntersectionObserver?其实它就是创建了一个观察的视口,当你要观察的元素进入到这个视口,就可以对这个元素进行操作,这个是发异步的,所以不用考虑耗性能的问题啦。
这次的列表数据,每个图片都要加上show:false。举个栗子
let list = [ { name:"aaaa", img:"aa.jpg", show:false }, { name:"bbbb", img:"bb.jpg", show:false }, { name:"cccc", img:"cc.jpg", show:false }, ]
列表数据大概就是这个意思。因为我的列表是发异步的到的,后面选择不同的条件,得到的列表数据会改变,所以我在setData的回调函数里进行操作的。
success: function (res) { //数据获取成功后,先渲染一遍 self.setData({ list:res.data.list, },function(){ //如果已经存在这个观察窗口,停止监听 if(self.intersectionObserver){ self.intersectionObserver.disconnect(); } //遍历得到的数组,对其进行操作 for(let i in res.data.list){ //创建一个视口,观察多个元素 self.intersectionObserver = swan.createIntersectionObserver(self,{ observeAll: true, }); //在视口里观察对应的元素,这边是.item0,.item1,.item2,.item3,... self.intersectionObserver.relativeToViewport().observe('.item-'+i, (ret) => { //ret.intersectionRatio > 0的意思是元素露出一点点 if(ret.intersectionRatio > 0){ res.data.list[i].show = true;//将列表里对应个数的图片显示出来 }; //将得到的新数组渲染到页面 self.setData({ list:res.data.list, }); }); } }); },
在页面退出后,要停止监听
onUnload: function() {
// 监听页面卸载的生命周期函数
let self = this;
if(self.intersectionObserver){
self.intersectionObserver.disconnect();
}
},
界面布局
<view s-for="{{list}}" s-key="i" s-for-item="n" class="item-{{index}}">
<view>
<image src="{{n.show ? n.logo : '/images/loading.gif'}}"></image>
</view>
</view>
这样做确实能实现效果,而且还不消耗性能。
问题来了
我页面中的list会根据条件的选择,获取到新的list进行刷新。第一次进入页面的时候完全没问题,然后在页面上选择了一个条件,得到一个新的list列表,但intersectionObserver观察到的不仅仅是这个新得的列表,还有我之前旧的列表。当我再选一个条件,intersectionObserver会观察3次的列表,而不是当前list。所以我怀疑是不是之前的视口没有关闭,一直在切换着观察,但我在创建之前已经把之前观察的窗口停止监听了self.intersectionObserver.disconnect();,打印出来看它的disconnect也是为true的。
我查了vue的intersectionObserver也是这么停止监听的,但在小程序里没有起到停止监听的作用,我不知道到问题出哪了,请看到这篇文章的大佬提点一下。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。