常见的业务需求,就是点击按钮,还没有反馈,你又点了一下。导致请求了两次。当然前端可以做按钮禁用,等待反馈。tab栏频繁切换。
如果不想看细节,直接使用,后面有完整的axios封装。
一、取消请求
原生
let xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10", true);
xhr.send();
setTimeout(() => xhr.abort(), 300);
axios
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10', {
cancelToken: source.token
})
source.cancel('取消请求.');
此外,你也可以通过调用 CancelToken 的构造函数来创建 CancelToken,具体如下所示:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10', {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
})
});
cancel(); // 取消请求
二、判断重复请求
当请求方式、请求 URL 地址和请求参数都一样时,我们就可以认为请求是一样的。因此在每次发起请求时,我们就可以根据当前请求的请求方式、请求 URL 地址和请求参数来生成一个唯一的 key,同时为每个请求创建一个专属的 CancelToken,然后把 key 和 cancel 函数以键值对的形式保存到 Map 对象中,使用 Map 的好处是可以快速的判断是否有重复的请求:
import Qs from "qs";
const pendingRequest = new Map();
// 用于根据当前请求的信息,生成请求 Key
function generateReqKey(config) {
const { method, url, params, data } = config;
return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}
function addPendingRequest(config) { // 每次请求追加该方法
const requestKey = generateReqKey(config);
config.cancelToken =
config.cancelToken ||
new axios.CancelToken((cancel) => {
if (!pendingRequest.has(requestKey)) {
pendingRequest.set(requestKey, cancel); // pendingRequest 对应好键 值
}
});
}
三、重复请求时取消
// 检查是否存在重复请求,若存在则取消已发的请求
function removePendingRequest(config) {
const requestKey = generateReqKey(config);
if (pendingRequest.has(requestKey)) {
const cancelToken = pendingRequest.get(requestKey);
cancelToken(requestKey);
pendingRequest.delete(requestKey);
}
}
四、整合axios封装
axios.js
文件
Ctrl+A Ctrl+C、Ctrl+V
即可(根据实际项目微调)
import axios from "axios";
import { get } from "lodash";
import Qs from "qs";
// 用于根据当前请求的信息,生成请求 Key
function generateReqKey(config) {
const { method, url, params, data } = config;
return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}
// 用于把当前请求信息添加到pendingRequest对象中
const pendingRequest = new Map();
function addPendingRequest(config) {
const requestKey = generateReqKey(config);
config.cancelToken =
config.cancelToken ||
new axios.CancelToken((cancel) => {
if (!pendingRequest.has(requestKey)) {
pendingRequest.set(requestKey, cancel);
}
});
}
// 检查是否存在重复请求,若存在则取消已发的请求
function removePendingRequest(config) {
const requestKey = generateReqKey(config);
if (pendingRequest.has(requestKey)) {
const cancelToken = pendingRequest.get(requestKey);
cancelToken(requestKey);
pendingRequest.delete(requestKey);
}
}
/**
* @description 创建请求实例
*/
function createService() {
// 创建一个 axios 实例
const service = axios.create();
// 请求拦截
service.interceptors.request.use(
(config) => {
removePendingRequest(config); // 检查是否存在重复请求,若存在则取消已发的请求
addPendingRequest(config); // 把当前请求信息添加到pendingRequest对象中
return config;
},
(error) => {
// 发送失败
console.log(error);
return Promise.reject(error);
}
);
// 响应拦截
service.interceptors.response.use(
(response) => {
removePendingRequest(response.config); // 从pendingRequest对象中移除请求
// dataAxios 是 axios 返回数据中的 data
const dataAxios = response.data;
// 这个状态码是和后端约定的 根据实际项目修改
const { status } = dataAxios;
// 根据 code 进行判断
if (status === undefined) {
return dataAxios;
} else {
// 有 code 代表这是一个后端接口 可以进行进一步的判断
switch (status) {
case 200:
return dataAxios.data;
default:
throw new Error(`${dataAxios.message}: ${dataAxios.data}`);
}
}
},
(error) => {
removePendingRequest(error.config || {}); // 从pendingRequest对象中移除请求
if (axios.isCancel(error)) {
console.log("已取消的重复请求:" + error.message);
} else {
// 添加异常处理
}
return Promise.reject(error);
}
);
return service;
}
/**
* @description 创建请求方法
* @param {Object} service axios 实例
*/
function createRequestFunction(service) {
return function(config) {
const token = localStorage.getItem("token");
const configDefault = {
headers: {
Authorization: "Bearer " + token,
"Content-Type": get(config, "headers.Content-Type", "application/json"),
},
timeout: 60000,
data: {},
};
return service(Object.assign(configDefault, config));
};
}
// 用于真实网络请求的实例和请求方法
export const service = createService();
export const request = createRequestFunction(service);
http.js
import { request } from "./axios";
/**
* post请求
*@param url 请求路径
*@param data 请求参数
*/
export function httpPost({ url, data = {} }) {
return request({
url,
method: "post",
data,
});
}
/**
* get请求
*@param url 请求路径
*@param params 请求参数
*/
export function httpGet({ url, params = {} }) {
return request({
url,
method: "get",
params,
});
}
/**
* put请求
*@param url 请求路径
*@param data 请求参数
*/
export function httpPut({ url, data = {} }) {
return request({
url,
method: "put",
data,
});
}
/**
* put请求
*@param url 请求路径
*@param data 请求参数
*/
export function httpDelete({ url, data = {} }) {
return request({
url,
method: "delete",
data,
});
}
五、测试
import { httpGet } from "../http/http";
export default {
created() {
this.getData();
},
methods: {
getData() {
httpGet({
url: "http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10",
}).then((res) => {
console.log(res);
});
httpGet({
url: "http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10",
}).then((res) => {
console.log(res);
});
httpGet({
url: "http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10",
}).then((res) => {
console.log(res);
});
httpGet({
url: "http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=11",
}).then((res) => {
console.log(res);
});
},
},
};
完工