实现思路:
把每次发送的请求及参数通过CancelToken创建标记添加到vuex的一个列表中,响应完成后从vuex的列表中删除该标记
每次添加标记到列表时先与列表比对是否重复,未重复正常添加,重复则不添加并取消该次请求。
通过循环vuex的请求标记列表,可以批量取消所有请求。
http.ts
该文件封装了axios的请求,对每次的请求通过cancel的createInterceptUrl方法创建标识,然后通过axiosPendingModule的addPending方法记录在vuex的列表中,响应结束后通过cancel的delayRepeatRequest从vuex中移除已经完成的请求
import axios from 'axios';
import qs from 'qs';
import { axiosPendingModule } from '@/store/modules/pending'; //请求连接状态列队
import { CancelToken, createInterceptUrl, removeRepeatRequest, delayRepeatRequest } from './cancel';
//ts接口
interface IParams {
[key: string]: any;
}
interface IHttp {
[key: string]: Function;
}
interface IStatusMessage {
[key: number]: string;
}
/* 通用设置 */
axios.defaults.timeout = 1000 * 30; //请求超时设置,30秒超时
axios.defaults.baseURL = 'http://localhost:8080';
/* 请求拦截 */
axios.interceptors.request.use(
(config: any) => {
//在一个axios发送前执行一下取消重复请求操作
removeRepeatRequest(config);
/* 创建取消请求的token,并把本次请求的信息添加到请求列表 */
config.cancelToken = new CancelToken((c) => {
axiosPendingModule.addPending({
u: createInterceptUrl(config),
f: c,
});
});
return config;
},
(error: any) => {
return Promise.reject(error);
}
);
//错误信息
let errorMessage: any = null;
/* 响应拦截 */
axios.interceptors.response.use(
(response) => {
//在一个axios响应后再执行一下取消操作,把已经完成的请求从pendingList中移除
delayRepeatRequest(response.config);
//状态码为200表示接口请求成功
if (response.status === 200) {
//code为0表示结果正常
if (response.data && response.data.code === 0) {
return Promise.resolve(response);
} else {
//异常状态码提示信息
errorMessage && errorMessage.close();
errorMessage = Message({
message: response.data.msg || 'Error',
type: 'error',
duration: 5 * 1000,
});
return Promise.reject(response);
}
} else {
return Promise.reject(response);
}
},
(error: any) => {
if (error.response && error.response.status) {
let statusMessage: IStatusMessage = {
400: '错误请求',
401: '未授权,请重新登录',
403: '拒绝访问',
404: '请求错误,未找到该资源',
405: '请求方法未允许',
408: '请求超时',
500: '服务器端出错',
502: '网络错误',
503: '服务不可用',
504: '网络超时',
505: 'http版本不支持该请求',
};
error.message = statusMessage[error.response.status] || `连接错误${error.response.status}`;
}
return Promise.reject(error);
}
);
/**
* 公共请求
* @param {String} type 请求类型
* @param {String} url 请求的url地址
* @param {Object} params 请求提交的参数
*/
function commonRequest(type: string, url: string, params: any = {}, config: any = { headers: { 'Content-Type': 'application/json' } }) {
return new Promise((resolve, reject) => {
let axiosParams: Array<any> = []; //axios方法请求的参数
if (UserModule.token) {
config.headers['Authorization'] = 'Bearer' + UserModule.token;
}
// get、delete共用请求
if (type === 'get' || type === 'delete') {
config[type === 'get' ? 'params' : 'data'] = params;
axiosParams = [url, config];
} else {
// post、put、patch共用请求
//不需要序列化参数的情况
let notStringify = config.headers['Content-Type'] === 'application/json' || config.headers['Content-Type'] === 'multipart/form-data';
//需要则qs序列化参数
if (!notStringify) {
params = qs.stringify(params, { arrayFormat: 'indices', allowDots: true });
}
axiosParams = [url, params, config];
}
(<IParams>axios)[type](...axiosParams).then(
(res: any) => {
resolve(res && res.data);
},
(err: any) => {
reject(err);
}
);
});
}
let http: IHttp = {};
let types: Array<string> = ['get', 'post', 'put', 'delete', 'patch'];
types.forEach((type: string) => {
http[type] = (url: string, params: any, config: any) => {
return commonRequest(type, url, params, config);
};
});
export default http;
pending.ts
该文件用于对记录在vuex的请求列表的增、删以及执行取消请求动作
import { VuexModule, Module, Mutation, Action, getModule } from 'vuex-module-decorators';
import store from '@/store';
@Module({ name: 'axiosPending', dynamic: true, namespaced: true, store })
export default class AxiosPending extends VuexModule {
public pendingList: any[] = [];
get getPendingList() {
return this.pendingList;
}
/* 添加请求到列表 */
@Mutation
private ADD_PENDING(pending: any) {
this.pendingList.push(pending);
}
/* 从列表删除取消请求 */
@Mutation
private REMOVE_PENDING(pending: any) {
debugger;
let index = this.pendingList.indexOf(pending);
index > -1 && this.pendingList.splice(index, 1);
console.log(index);
pending.f('取消请求'); //执行取消操作
}
/* 添加请求到列表 */
@Action
public addPending(request: any) {
this.ADD_PENDING(request);
}
/* 从列表删除取消请求 */
@Action
public removePending(pending: any) {
this.REMOVE_PENDING(pending);
}
/* 取消所有请求 */
@Action
public cancelAllPending() {
for (let i = this.pendingList.length - 1; i >= 0; i--) {
this.REMOVE_PENDING(this.pendingList[i]);
}
}
}
export const axiosPendingModule = getModule(AxiosPending);
cancel.ts
该文件封装了创建请求标识、移除重复请求、延期请求等一些常用的操作方法
import axios from 'axios';
import qs from 'qs';
import { axiosPendingModule } from '@/store/modules/pending';
/* 取消请求设置 */
export const CancelToken = axios.CancelToken;
/**
* 创建用于拦截请求的标识,为保证请求信息完整,组合了请求的地址、类型、参数
* @param {Object} config axios请求的配置对象
* @returns {String} 返回用于拦截的字符串标识
*/
export function createInterceptUrl(config: any) {
//处理FormData不能直接被序列化的问题
let dataStringify = '';
//formdata特殊处理
if (config.data instanceof FormData) {
let file = config.data.get('file');
dataStringify = qs.stringify(file);
} else {
dataStringify = qs.stringify(config.data);
}
let url = config.url + '&' + config.method + '&' + qs.stringify(config.params) + '&' + dataStringify;
return url;
}
/**
* 移除重复请求
* @param {Object} config 请求配置对象
*/
export function removeRepeatRequest(config: any) {
let currentPending = createInterceptUrl(config); //本次请求信息
let pendingList = axiosPendingModule.getPendingList;
pendingList.some((pending) => {
//历史请求信息(请求的config.url中不包含baseUrl,但响应中的url会包含baseUrl,此处先处理两者差异)
let historyPending = config.url.includes(axios.defaults.baseURL) ? axios.defaults.baseURL + pending.u : pending.u;
if (currentPending === historyPending) {
axiosPendingModule.removePending(pending);
}
});
}
/**
* 延迟请求,相同请求不得在短时间内重复发送
* @param {Object} config - 请求配置
*/
export function delayRepeatRequest(config: any) {
setTimeout(() => {
removeRepeatRequest(config);
}, 500);
}
export default {
CancelToken,
createInterceptUrl,
removeRepeatRequest,
delayRepeatRequest,
};
取消所有请求应用示例
import { axiosPendingModule } from '@/store/modules/pending';
axiosPendingModule.cancelAllPending(); //取消所有请求