封装取消请求逻辑
取消请求场景:
- 当请求发送了多次,需要取消之前的请求
- 当路由切换时,需要取消上个路由中未完成的请求
封装储存请求,清除请求方法
/** 声明一个对象 用于存储每个请求的标识 和 取消函数 */
const pending = new Map()
/** CancelToken 是 axios自带的取消请求函数 **/
let CancelToken = axios.CancelToken
/**
* 添加请求
* @param {Object} config
*/
const addPending = (config) => {
/** 加上参数识别,同个接口可能请求多次的情况 **/
const url = [ config.method, config.url, qs.stringify(config.params), qs.stringify(config.data) ].join('&')
config.cancelToken = new CancelToken((cancel)=>{
pending.set(url, cancel)
});
}
/**
* 移除请求
* @param {Object} config
*/
const removePending = (config) => {
const url = [ config.method, config.url, qs.stringify(config.params), qs.stringify(config.data) ].join('&')
/** 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除 */
if (pending.has(url)) {
const cancel = pending.get(url)
cancel(url)
pending.delete(url)
}
}
/**
* 清空 pending 中的请求(在路由跳转时调用)
*/
export const clearPending = () => {
for (const [url, cancel] of pending) {
cancel(url)
}
pending.clear()
}
axios拦截器
把上面封装好的方法放到axios拦截器中
/**
* request 拦截器
*/
instance.interceptors.request.use(config => {
/** 可以在请求响应器设置接口的 headers */
const TOKEN= getToken() || '';
config.headers = {
TOKEN,
'Content-Type': config.contenType ? config.contenType : 'application/json;charset=UTF-8'
}
/** 在请求开始前,对之前的请求做检查取消操作 */
removePending(config)
/** 将当前请求添加到 pending 中 */
addPending(config)
return config
}, error => {
return Promise.reject(error)
})
/**
* response 拦截器
*/
instance.interceptors.response.use(response => {
/** 在请求结束后,移除本次请求 */
removePending(response.config)
/** 在这里可以做相关状态处理,比如登录失效,跳转登录页面等 **/
if (response.data.code === -100) {
/** 清空用户缓存信息 */
store.dispatch('user/logout')
/** 跳转到登录页 */
router.replace('/login?redirect=' + encodeURIComponent(window.vm.$route.fullPath))
}
return response.data
}, error => {
/** 判断终止请求的不返回error **/
if (axios.isCancel(error)) {
console.log('repeated request: ' + error.message)
} else {
// handle error code
return Promise.reject(error)
}
})
/**
* 使用封装好的 axios,并暴露出去
**/
const server = (ajaxParams) => {
if (typeof ajaxParams !== 'object' || Array.isArray(ajaxParams)) throw new Error('参数类型必须是object,请检查')
/** 合并请求参数 */
let parame_ = Object.assign({}, ajaxParams.data || {})
const method = ajaxParams.method === 'GET' ? 'params' : 'data';
/** 判断 是否为表单请求 Content-Type:multipart/form-data */
if (!ajaxParams.contenType) {
ajaxParams[method] = parame_
} else {
/** 非文件/图片上传 并且需要表单上传参数 */
/** 表单请求参数需要序列化 */
if (!ajaxParams.data instanceof FormData) {
ajaxParams[method] = qs.stringify(parame_)
}
}
return instance(ajaxParams)
}
export default server
使用
在main.js中引入并挂载
import server from '@/assets/js/request.js'
Vue.use($ajax);
在文件中使用
/** 普通请求 **/
this.$ajax({
url: '',
method: 'POST',
data: {}
}).then((res) => {}).catch(err => {});
/** 实例 图片/文件上传 **/
let formData = new FormData()
formData.append("imageFile", file.file)
this.$ajax({
url: '',
method: 'POST',
contenType: 'multipart/form-data',
data: formData
}).then((res) => {}).catch(err => {});
/** 表单请求 **/
this.$ajax({
url: '',
method: 'POST',
contenType: 'multipart/form-data',
data: {}
}).then((res) => {}).catch(err => {})
参考文章 https://segmentfault.com/a/1190000021290514