mint-ui是饿了么提供的一套vue移动组件,使用起来还是很方便的。

其中有一类组件是用于分页加载数据的,比如Infinite Scroll和Loadmore这两个组件。

最近同事在使用Infinite Scroll实现数据分页懒加载时,遇到了一个奇怪的坑,我花了不少时间才定位出来原因,今天就写到博客里记录一下避免以后再次踩坑。


先来看看Infinite Scroll的介绍:

Infinite Scroll的主要属性为v-infinite-scroll和infinite-scroll-distance,v-infinite-scroll为触发加载时执行的函数,infinite-scroll-distance用来指定滑动到距离页面底部多长的距离时触发加载函数。

infinite-scroll-distance的值较大时,触发加载的滑动距离就可以越短,infinite-scroll-distance的值小则需要滑动到更接近底部的位置才会触发加载。

上面的例子实现的效果如下:

1.滑动到页面底部时触发加载:

2.加载完成:


下面是同事写的有问题的代码的大致示例:

...
data:function(){
    return {
        pageSize:8,//每页数据包含8条记录
        pageNum:0,//当前请求数据页号
        list:[],//记录集合
        ifHasData:true//为true时展示数据,为false时展示空页面
    };
},
...
loadMore:function(){
    this.pageNum = this.pageNum+1;
    this.getData();
},
getData:function(){
    //执行一个异步的ajax请求,并在回调中操作返回的数据
    doGet(url,{
        pageNum:this.pageNum,
        pageSize:this.pageSize
    }).then(function(datas){
        if(datas.length>0){
            //将加载到的数据合并到list中
            this.list = this.list.concat(datas);
        }
        //如果list中没有数据可以展示,那么将展示空页面
        if(this.list.length == 0){
            this.ifHasData = false;
        }
    })
}
...

其中loadMore方法被配置为Infinite Scroll组件在下滑触发加载时的事件,每次执行loadMore都去用getData用来获取下一页的数据。

这份代码看上去并没有什么问题,但是同事每次执行上面的代码时,后台明明有10条数据可以展示( 分成两页,一页8条,一页2条 ),但是不知道为什么每次都展示空页面。

debug后发现,刚进入这个页面时,由于Infinite Sroll组件可能由于页面没有高度的原因,触发了三次加载,执行了三次loadMore方法。也就是去请求了三次数据,但是看了上面的代码发现即使是执行了三次请求,也并没有什么问题啊,大不了就把10条数据一次全部加载出来而已。

思考了好久之后,我无意间看了一下NetWork里面这三次数据请求的执行返回时间,发现由于一共只有10条数据也就是两页数据的原因,第三次请求虽然是最后发出的,但却是第一个执行完毕返回的,因为数据库里没有第三页的数据,导致第三次请求的执行速度比前两次的块。

然后再来看看上面代码中的这一段:

if(datas.length>0){
    //将加载到的数据合并到list中
    this.list = this.list.concat(datas);
}
//如果list中没有数据可以展示,那么将展示空页面
if(this.list.length == 0){
    this.ifHasData = false;
}

会发现当list的length为0时,代码将ifHasData的值置为false了,当ifHasData的值为false时就会展示空页面。

说到这里,问题就一目了然了,由于ajax异步请求的原因,导致没有数据的一次请求快与之前的请求返回,然后将将ifHasData的值置为false,之后的请求返回后并没有再将ifHasData的值置为true,导致页面上一致显示的空页面。当然如果第一、二、三次请求是同步请求的话,那么就不会存在上面这种问题。

这个问题的解决方法,再加一个判断,当发现有数据时将ifHasData再置为true即可:

if(datas.length>0){
    //将加载到的数据合并到list中
    this.list = this.list.concat(datas);
}
//如果list中没有数据可以展示,那么将展示空页面
if(this.list.length == 0){
    this.ifHasData = false;
}else{
    this.ifHasData = true;
}

这样在前两次“姗姗来迟”的请求返回,使得list有值之后,会再将ifHasData置为true,这样就能正常展示数据了。

当然我更推荐的一种做法,是使用一个计算属性:

...
computed:{
    ifHasData:function(){
        return this.list.length >= 0;
    }
}
...

使用计算属性的好处是每当this.list的值发生变化时,ifHasData计算属性的值都会随之更新,因此就不需要再在代码里手工判断了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注