在项目中经常会遇到需要主动取消接口的场景,axios提供CancelToken的功能可以主动停止当前请求,从而避免无效请求,优化网络性能
场景:
- 远程搜索接口频繁请求,第二个接口先成功导致显示第一个接口返回的数据
- 前端切换路由,前一个路由的接口用不到了,但还在请求中
这两种情况可以单独在组件中使用CancelToken来取消接口请求,但是如果要主动请求的接口较多,则会照成代码冗余,于是需要封装成一个公共的方法
1、搭载请求容器及处理方法:cancel-request.js
-
pengdingList: 盛放接口处于pengding(请求中)状态的容器,key是接口唯一标识,value是该接口的cancel请求的方法
-
getFetchKey: 生成接口唯一标识的方法
-
addPending:把请求加到pending容器的方法
-
removePending:把请求从pengding容器中移除的方法
-
cancelPending:取消请求并从pengding中移除的方法
/**
* 重复请求取消上次请求
*/
import axios from 'axios'
// 存储pengding请求容器
export const pendingList = new Map()
// 生成各个请求标识
export const getFetchKey = (config) => {
const { url, data, method } = config
let token
if (method === 'get') {
token = [method, url].join('&')
} else {
token = [method, url, JSON.stringify(data)].join('&')
}
return token
}
// 添加pengding请求
export const addPending = (config) => {
const fetchKey = getFetchKey(config)
if (fetchKey) {
config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {
if (!pendingList.has(fetchKey)) {
pendingList.set(fetchKey, cancel)
}
})
}
}
// 移除pengding请求
export const removePending = (config) => {
const fetchKey = getFetchKey(config)
if (fetchKey) {
if (pendingList.has(fetchKey)) {
pendingList.delete(fetchKey)
}
}
}
// 取消请求
export const cancelPending = (config) => {
const fetchKey = getFetchKey(config)
if (fetchKey) {
if (pendingList.has(fetchKey)) {
const cancel = pendingList.get(fetchKey)
cancel('cancel')
pendingList.delete(fetchKey)
}
}
}
2、axios拦截器配置-防止频繁请求
- 请求时判断是否该请求是否重复,如果重复取消之前的请求
- 请求时把当前请求加入pending容器中
- 相应成功或失败时把当前请求从pending容器中移除
/**
* axios拦截器配置
*/
import {
addPending, removePending, cancelPending,
} from '@util/cancel-request'
import axios from 'axios'
// 请求拦截器
axios.interceptors.request.use(
request => {
// 发送请求前首先检查该请求是否已经重复,重复则进行取消并移除
cancelPending(request)
// 添加该请求到pendingList中
addPending(request)
return request
},
error => {
return Promise.error(error)
}
)
axios.interceptors.response.use(
response => {
// 请求成功去除pengding状态
removePending(response)
if (response.status === 200) {
return Promise.resolve(response)
} else {
return Promise.reject(response)
}
},
error => {
// 请求失败去除pengding状态
removePending(error.response)
return Promise.reject(error.response)
}
)
3、切换路由取消之前页面的pending状态的请求-beforEach(react用路由组件判断)
- 在beforEach方法中获取所有pending状态的接口(pengding容器)
- 对pending容器进行循环,依次取消
- 清空pengding容器
import { pendingList } from '@util/cancel-request'
router.beforeEach((to, from, next) => {
let pengdingArr = pendingList.values()
let current = pengdingArr.next()
while (!current.done) {
current.value('cancel')
current = pengdingArr.next()
}
pendingList.clear()
next()
})