赞
踩
在vue类型的项目开发中,我们一般都是发起异步请求从服务器获取数据后,根据数组数据使用v-for来动态渲染数据列表。
但是,如果一个请求在pending中,再次发送一个请求,最后导致渲染的list,数据重复,或是错误的问题。
原因,就是多次请求了异步接口,一个接口没有返回,另外一个接口就发出去了。因为,ajax是一个异步操作。导致,在回调的时候,两次请求成功后的回调都会执行。就导致数据,错误了。
什么情况下发生这种现象呢? 譬如下拉滚动加载更多 或是 tab切换。
类似,这种,点击tab标签,根据list数据来渲染列表。
当快速切换tab标签时(可以把调试的网速降低,更容易看到这种情况),导致前一个标签的内容也会显示在第二个标签的内容里。
面对这种多次触发异步请求的处理,常用的解决办法如下:
譬如,表单提交了,就把表单提交按钮disable禁用,不然再次提交。
譬如在微信小程序中使用:
wx.showLoading({
title: '加载中',
mask: true
})
具体就上面这个问题,分析一下。
我在修改前的代码如下:
// 获取列表数据 fetchList () { this.loading = true wx.showLoading() let para = { curUserId: this.userId, page: this.page, customerName: this.keyword, orderStatus = this.active } fetchReportRecordList(para) .then((res) => { wx.hideLoading() const data = res.data this.total = data.total this.list = [...this.list, ...data.rows] this.loading = false }) .catch(() => { this.loading = false }) },
导致的原因就是,标签一的请求发出去了,还没有回来,我就切换到标签二去了。这两个异步操作,都会返回数据,都会执行.then
后面的回调。你可能觉得,在回调执行前把list = [] 不就可以了么? 其实是不行的,因为.then是异步的回调,异步请求发出前的函数,是同步执行的。
那如何解决了?
思路就是,发出请求时做一个标记,结果返回的时候,和标记对比一下,是不是我要的,如果已经不是我要的请求结果了,就直接忽略掉这次的请求结果。
面对上面的这个问题,我只需要在发出请求的时候,把当前tab激活的标签编号记录下来,等请求结果返回的时候,对比一下,看发请求的时候的标签编号,和现在的激活标签是不是同一个。
如果是,就添加到list中去,如果不是,那么这次请求结果就不能用了。直接扔掉请求结果。
改写后的代码:
// 获取列表, fix tab快速切换,一个请求pending时再次发送一个请求,导致的list数据错误的问题 fetchList() { let that = this // 缓存this,表示当前vue 实例,return funtion中的,this会变了。 return (function (j) { // 接收立即执行函数传递过来的参数 // console.log(j, that.active) that.loading = true wx.showLoading() let para = { curUserId: that.userId, page: that.page, customerName: that.keyword, orderStatus = that.active } fetchReportRecordList(para) .then((res) => { wx.hideLoading() // 如果发出请求的时候的tab id 和 目前处于激活状态的tab id 不一样了,说明,结果已经不能要了,直接放弃。 if(j !== that.active) { return false } const data = res.data that.total = data.total // 请求结果可用,就追加到list后面。我这里这个方法,是有滚动到底部,触发加载更多。所有,采用追加在尾部的方式。 that.list = [...that.list, ...data.rows] that.loading = false }) .catch(() => { that.loading = false }) })(that.active) // 立即执行函数,并传参进去形成闭包 },
添加一个立即执行函数和闭包,把当前激活的tabthat.active
传递进去,用参数j
接收起来。在返回结果的时候,去比较 j 和 that.active。
如果在滚动下来加载中,遇到第一页数据加载了两次,这种类似的。都可以用这种方法解决。就是通过判断页码了。
针对上面的问题,另外一个比较好的主流的解决办法。就是一个tab就定义一个list,不要使用同一个list来装数据。就可以避免上面的那个问题了。
具体的代码如下:
const params = { rows: [], page: 0, status: null, loading: false, loaded: false, customerName: '', } export default { data() { return { params: JSON.parse(JSON.stringify(params)), } } } fetchList() { let item = this.params if (item.loading || item.loaded) return false let page = item.page, rows = item.rows page++ wx.showLoading() let para = { curUserId: this.userId, page: this.page, customerName: this.keyword, orderStatus = this.active } fetchReportRecordList(para) .then((res) => { wx.hideLoading() const data = res.data this.total = data.total rows = [...rows, ...data.rows] item.rows = rows item.page = page item.loading = false if (rows.length === res.total) { item.loaded = true } this.params = item }) .catch(() => { }) },
那么渲染数据的时候,就采用下面这样:
<div v-for="(item, index) in params.rows" class="card" :key="index" @click="linkToDetial(item)">
{{item.title}}
</div>
对应的,tab切换的时候,原来公用的list = [] 这种方式,也需要改为this.params = JSON.parse(JSON.stringify(params))这种重新赋值。
// 重新获取数据
loadTop() {
// this.list = []
this.params = JSON.parse(JSON.stringify(params))
// this.params = Object.assign({}, params)
this.page = 1
this.fetchList()
},
解决的本质,就是一个列表对应一个list来保存数据,不再公用一个list了。并且通过JSON.parse(JSON.stringify(params))来完成深拷贝,切断引用类型的联系。
另外说一句,Object.assign({}, params) 也可以。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。