一、大致思路
一旦生成请求,将请求以key, value的形式存入一个Map数据类型的容中,然后在请求拦截器中先对当前请求在Map中进行查重处理,重复则调用AbortControl.abort()取消;如果没重复,则加入到Map中。
二、开整
1.封装一个Map容器类 PendingRequestMap
// cancelPendingRequest.js
import axios from 'axios'
import qs from 'qs'
class PendingRequestMap {
constructor() {
this.map = new Map()
// this.controller = new AbortController()
}
// 生成唯一key
getRequestKey(config) {
const { url, method, params, data } = config
const key = [url, method, qs.stringify(params), qs.stringify(data)].join('#')
return key
}
// 将新的请求放入map中
addPendingRequest(config) {
const key = this.getRequestKey(config)
config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
if (!this.map.has(key)) {
this.map.set(key, cancel)
}
})
}
// 移除map中的请求
removePendingRequest(config){
const key = this.getRequestKey(config)
if (this.map.has(key)) {
const cancel = this.map.get(key)
cancel('canceled')
this.map.delete(key)
}
}
// 取消所有pengding请求
clearAllPendingRequest() {
// 遍历,挨个取消请求
for (const cancel of this.map.values()) {
cancel('canceled')
}
// 清空容器
this.map.clear()
}
}
export default new PendingRequestMap()
AbortController版本
// cancelPendingRequest.js
import qs from 'qs'
class PendingRequestMap {
constructor() {
this.reqMap = new Map()
}
// 生成唯一key
getRequestKey(config) {
const { url, method, params, data } = config
const key = [url, method, qs.stringify(params), qs.stringify(data)].join('#')
return key
}
// 将新的请求放入map中
addPendingRequest(config) {
const key = this.getRequestKey(config)
if (!this.reqMap.has(key)) {
const controller = new AbortController()
config.signal = controller.signal
this.reqMap.set(key, controller)
}
}
// 移除map中的请求并取消
removePendingRequest(config) {
console.log(config, 'config');
const key = this.getRequestKey(config)
if (this.reqMap.has(key)) {
let controller = this.reqMap.get(key)
controller.abort()
controller = null
this.reqMap.delete(key)
}
}
// 取消所有pending请求
clearAllPendingRequest() {
// 遍历,挨个取消请求
for (let controller of this.reqMap.values()) {
controller.abort()
controller = null
}
// 清空容器
this.reqMap.clear()
}
}
export default new PendingRequestMap()
2.在request.js中调用
请求前对重复请求操作和将新的请求放入容器中,请求结束后移除
// request.js
import pendingRequestMap from '@/utils/cancelPendingRequest.js'
/**
* 请求拦截器
*/
axios.interceptors.request.use(config => {
// 在请求开始前,对之前的请求做检查取消操作
pendingRequestMap.removePendingRequest(config)
// 将当前请求添加到 pending 中
pendingRequestMap.addPendingRequest(config)
// do something.....
return config
}, error => {
return Promise.reject(error.response)
})
/**
* 响应拦截器
*/
service.interceptors.response.use(response => {
// 在请求结束后,移除本次请求
pendingRequestMap.removePendingRequest(response.config)
// do something...
return Promise.resolve(response.data)
}, error => {
return Promise.reject(error.response.data)
})
3.跳转页面后,取消上个页面未完成的请求
在路由全局导航守卫中这么写
import pendingRequestMap from '@/utils/cancelPendingRequest.js'
router.beforeEach(async (to, from, next) => {
//清空pending请求
pendingRequestMap.clearAllPendingRequest()
next()
}
结语
代码中如有需要更正和修改的地方,欢迎评论讨论。