说明
在项目测试过程中有一次网络不是很好,测试人员对一个请求多次点击,然后切换路由后,由于上个页面堆积的请求过多,导致该页面请求返回缓慢.之前是没有考虑过要终止,毕竟这也是遇到了,也算是优化.然后我去百度找了各路大佬整理的,发现,请求是终止了,但是新页面的请求也被终止了,于是我就自己研究了一下,对代码进行了自己想法上的整理.(PS:以下代码已经过实践!!如有问题无法实现的可以留言).
代码与讲解
一 思路
1.将所有的请求都收集起来(我这里通过VueX管理这个请求数组).
2.在路由切换的时候通过cenael()进行终止(我用的是axios)
二 代码(以下......省略号为省略代码,与此次实现无关)
1.首先是vuex的代码,
讲解:建立一个请求数组,将请求都装入其中,这里存放在vuex的state中,代码如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state:{
requests:[], // 请求队列
}
})
export default store
也可对数据处理进行统一的封装
......
mutations: {
pushRequests(state, source) {
state.requests = [...state.requests, source];
},
emptyRequests(state) {
if (Setting.cancelPendingReq) {
state.requests.forEach((xhr) => xhr.cancel());
state.requests = [];
}
}
}
我采用的是modules嵌入vuex,调用示例
// request.js
store.commit('global/api-cancel/pushRequests', source);
// 路由拦截中
store.commit('global/api-cancel/emptyRequests');
2.然后是axios的代码
讲解:我项目是单独封装了request.js所以axios内,代码片段如下:
import axios from 'axios' // 引入axios
import store from '@/store' // 引入store
var CancelToken = axios.CancelToken; // 申明CancelToken
const service = axios.create({
......
}) // 建立一个axios ,里面参数根据自己需要配置
// 请求拦截
service.interceptors.request.use(
config => {
let source = CancelToken.source() // 申明CancelToken,也可new CancelToken.source()实例一个
config.cancelToken =source.token //讲实例对象的token赋予该请求
store.state.requests.push(source) // 将该实例添加到队列中
......
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器(响应拦截在这里其实没什么作用,只不过使用cancel()时候我发现有个不小心会出错的问题)
service.interceptors.response.use(
response => {
const res = response.data
return res
},
error => {
// 当cancel()里面没有传参的时候message返回的是undefined,我有专门对error.message进行includes判断这时候我发现报错了,所以我建议大家可以和我一样加一个判断
if(!error.message){
return Promise.reject(error)
}
.......
}
)
export default service
3.路由守卫的代码
讲解:全局路由前守卫中,每次切换终止请求,并清空队列,代码片段如下:
import router from './router'
import store from './store'
router.beforeEach(async (to, from, next) => {
store.state.requests.forEach(xhr=>xhr.cancel()) // 通过遍历终止所有未完成的请求
// 其中xhr=>xhr.cancel()为下面注释代码的简写,
//(xhr) => {
// return xhr.cancel
//}
store.state.requests =[] // 执行完清空,等待下一次的页面的请求装载
next()
})
......
4.其他补充
讲解:当请求被终止的时候,catch中err会被打印,所以加个判断以免一终断就打印一堆,我是这么处理的,代码如下:
.......
catch (err) {
if(err != 'Cancel'){
console.log("debug", err);
}
}
或者在请求拦截中全局配置请求终止的拦截
......
// 判断是否为请求终止
if (error != 'Cancel') {
Message.error({
content: '系统出错了,请稍后重试',
});
} else {
return new Promise(() => {}); // 抛出一个pending的Promise使得结果不进入then也不进入catch,后续需要注意的是如果存在结果then或catch接受请适当挑战
}
三.总结
1.在整理代码过程中由于对axios的了解不够深入,并不知道为什么要这么做,我的理解是,要给每个请求绑定一个token(source.token),然后将对应的source进行添加队列,也是通过终止source就会终止响应token的请求.
2.以下感谢能看到这篇文章的同学们,希望对你们有帮助.