filter实现图片预加载

针对图片列表,我们往往在进行图片请求时,防止默认的占位图,等图片加载成功再将图片进行替换,防止列表抖动以及图片加载失败等情况,给用户更好的体验。本次我们将介绍如何通过filter对v-for遍历的图片列表进行图片预加载。

举个栗子

我们往往会使用以下方式来生成一个图片列表,使用v-for进行遍历

1
2
3
4
5
<ul>
<li v-for="item in list" >
<img :src="item.imgURl">
</li>
</ul>

使用preLoad方式进行图片预加载并进行错误处理,手动创建一个img,当加载成功时,将img标签进行返回,失败时返回错误回调。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export const preLoad = function(url, onSuccess, onerror) {
var img = new Image();
img.src = url;
img.onload = function () {
if(typeof callback == "function"){
onSuccess(img);
}
}
img.onerror = function () {
if(typeof onerror == "function"){
onerror();
}
}
};

如何将这两个方式进行整合呢?

可以发现,我们在遍历过程中,很难针对图片URL进行操作,并对其进行修改。因此我们初期采用这种方式进行预加载处理,首先对列表进行遍历,分别加载对应图片,加载成功后将元素push到数组中,失败时对其进行处理,像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pushData(data){
const that = this;
//对于热加载失败的图片使用默认图片替换
data.map(function(elem, index) {
let onSuccess = function () {
that.fontList.push(elem)
}
let onError = function () {
elem.imgUrl = defaultUrl;
that.fontList.push(elem);
}
preLoad( elem.imgUrl, onSuccess , onError);
})
}

问题

上种方式可以很好的将加载失败的图片替换成默认图,而这中方式往往会带来以下问题,并没有充分满足我们的需求。

  • 加载图片成功之后才会一个个显示到列表中,体验较差
  • 异步加载图片,列表顺序会乱。对于排行榜等不适用
  • 未加载成功时,没有占位图

优化

后来我想到,针对引用变量,我们可以随时对其进行赋值,从而可以满足我们先展示,加载后修改其值的需求,类似这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pushData(data){
//对于热加载失败的图片使用默认图片替换
data.map((elem, index) => {
let onSuccess = function (img) {
//成功后替换默认图
Vue.set(item, 'imgUrl', img.src);
}
//设置默认占位图
item.imgUrl = defaultUrl;
//预加载图片
preLoad( elem.imgUrl, onSuccess);
//展示
this.fontList.push(elem);
});
}

使用filter

上述方法可以很好的满足我们的需求,但是往往会导致代码混杂,可读性很差。我们仅仅是对数组进行添加、替换等操作,但是看起来做了N多的操作。如何将代码更优雅的抽离出来,自动对其进行处理呢?试试filter吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Vue.filter('preload', function(url, item, defaultUrl){
//未加载
if( !item.hasPreLoad ) {
//加载
preLoad( url, (img)=>{
//防止修改原字段,使用preLoadUrl代替。
Vue.set(item, 'hasPreLoad', true);
Vue.set(item, 'preLoadUrl', img.src);
});
//返回默认
return defaultUrl;
} else {
//已经加载
return item.preLoadUrl;
}
});

借用filter将传入的url进行过滤,为了防止修改原有字段值,我们使用新的字段来进行图片的展示。通过这样,我们可以很方便的实现列表的预加载和占位图功能,代码清晰,方便好用。

1
2
3
4
5
<ul>
<li v-for="item in iconList">
<img :src="item.show | preload item defaultImgUrl">
</li>
</ul>

效果如下:

filter

最后希望自己能够通过慢慢的积累,不断的提升自己的能力。