實現思路:
把每次發送的請求及參數通過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(); //取消所有請求