最近遇到一个业务场景,tabs切换三个标签页。每切换一个tabs,就会请求一次数据,然后把数据放到已经清空的一个数组内。那么因为有的数据多,有的少。就会造成第二个tabs的数据展示到第三个tsbs内。虽然这些数据会在切换时候拨乱反正。但这是个大问题。
前端demo
当快速切换tab时如果这时用户终端设备网络不好,或者两个tab请求返回数据的时差先后问题,就会导致类似标签2展示的是标签三的数据
解决方法
这里我就认为大家都知道什么是数据竞态了,如果还有什么不懂得地方就看看大厂们怎么说的
# 飞书:前端提到的竞态问题,在 Android 上怎么解决?
1...添加Loaing(禁用页面)
这是最简单的方法,但坏处就是如果时间过久,对用户体验的感觉不好,但是阻断了一切问题的发生
2...忽略不匹配的返回数据
比如:我们在一个tabs标签页切换的时候都会有一个activeIndex变量,我们只需要在请求返回数据的赋值时候进行判断是否是此tab页签的数据在进行赋值即可,这样子也可以解决
我这里是有将activeIndex进行传参给后端的,所以在拿到res后可以用局部作用域的params和全局的activeIndex进行判断
js
// 获取表格数据
async getTableDatas() {
try {
//......忽略前面的操作
let res = await getRefundList(params)
// 💥💥💥防止切换太快导致的数据不一致
if (params.conditionsMap.reviewStatus === this.activeIndex) this.tableData = res.data.data.data;
}
},
3...Axios 取消请求
我这里只写了关于Axios (v0.22.0)以后版本的取消请求方法,因为再次之前的取消请求是通过CancelToken Api的方法,但是在0.22.0版本就开始被弃用了
从 v0.22.0
开始,axios 支持以 fetch API 方式的 AbortController 取消请求
js
// 这是官方写法
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// 取消请求
controller.abort()
当然写的太关于简单了,意思就是先使用new AbortController() 创建一个实例,将这个实例传给axios方法的最后一个参数signal,就可以将该请求与该实例绑定起来,后面需要取消的话只需调用实例的abort()方法即可取消请求,我不太喜欢写在拦截器中,感觉不好单独控制(当然这是我个人的代码风格,如果以后需要优化的话在弄吧 touch 🐟)
js
// .vue文件
// 取消请求
let controller = ref(null)
const handleClick = async () => {
try {
//在下一次请求前判断实例是否有请求
if (controller.value) controller.value.abort()
controller.value = new AbortController()
let res = await cancelAxios(
{ id: activeTab.value },
{ signal: controller.value.signal }
)
data.value = res.data
controller.value = null // 清空实例对象
} catch (error) {
console.log(error)
}
}
js
// .request文件
import request from '../index'
// post就将signal参数传给最后一个参数即可 ,如果是get传给第二个参数即可
export const cancelAxios = (data, signal) => request.post('/cancel', data, signal)
export const getCancelAxios = (signal) => request.get('/getCancel', signal)
总结
根据实际情况选最适合自己的方法即可,各有各的好,哪个方便就用哪个就好了(如果还有其他更好的方法,可以再评论区讨论讨论)