这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战
对于axios的请求封装大家都不陌生,可能更多就是调用create
方法创建实例和对该实例的拦截器interceptors
方法进行重写。但有时候还需要在此基础上添加取消重复请求的功能,接下来来实现这个功能。
axios请求简单封装
首先对axios进行简单的封装,为了代码的简洁易懂,和拦截器相关的业务逻辑就不在这里添加。
import axios from 'axios';
const instance = axios.create({
// ...
});
// 请求拦截器
instance.interceptors.request.use(config => {
return config;
}, err => {
return Promise.reject(err);
});
// 响应拦截器
instance.interceptors.response.use(res => {
return res.data;
}, err => {
return Promise.reject(err);
});
export default instance;
取消重复请求
取消普通请求
首先对普通的XMLHttpRequest
请求进行取消,这里用到了abort()
方法,具体代码如下:
var ajax = new XMLHttpRequest();
ajax.open('GET', '/abc');
ajax.send();
setTimeout(() => {
ajax.abort(); // 取消当前请求
}, 300);
取消axios请求例子
取消axios
请求,这里用到了axios
内部的cancelToken
方法,具体代码如下:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).then(res => {});
// cancel方法里面的参数是可选参数
source.cancel('Operation canceled by the user.');
除了上面的方法以外,axios
官方还有另外一种方式:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// 这里的c就是取消当前请求的方法,这里把c赋值给cancel变量
cancel = c;
})
});
// 调用取消方法
cancel();
接下来对axios
取消请求的方式就是用到上面这种,通过CancelToken
构造函数上的参数来获得取消当前的请求的能力。
实现取消重复请求类
实现思路:
取消重复请求,这里主要针对的是多个重复请求发送的情况,例如在同一次操作中,发送了两个相同的请求,那么这个时候就会对前一个请求进行取消操作,真正发送出去的是后一个请求。
怎么判断请求重复了呢?这里可以通过请求的地址url
,请求方式method
,请求参数params|data
这几个值来做唯一判断。如果两个请求上的这些信息都相同,那么就可以判断这两个请求是重复的。
请求如果重复,接下来该做什么?当然是调用cancel
方法进行取消,但是怎么知道这个请求的cancel
方法如何获取呢?其实这里就是怎么把当前请求和对应的cancel
进行关联,这里可以用对象(Object
)或者Map
进行存储,把当前请求的信息作为key
,对应的cancel
方法作为value
,存储到对象或者Map
中,然后就可以根据当前请求信息或者到对应的取消方法。这里每一个请求的cancel
方法需要调用new CancelToken()
才可以获取。
代码实现(CancelRequest.js):
import qs from 'qs'; // 引入qs用来序列化请求参数
export default class CancelRequest {
constructor(map) {
this.cancelReqMap = this.isMap(map) ? map : new Map();
}
// 判断是否是map数据格式
isMap(data) {
if (Object.prototype.toString.call(data) === "[object Map]") return true;
}
// 根据请求配置信息`config`进行拼接,生成由`url、method、params、data`组成的字符串
generateCancelReqKey(config) {
let { url, method, params, data } = config;
return [url, method, qs.stringify(params), qs.stringify(data)].join('&');
}
// 添加请求信息和对应的取消方法到cancelReqMap属性
addCancelReqKey(config, cancelToken) {
const cancelReqKey = this.generateCancelReqKey(config);
config.cancelToken = config.cancelToken || new cancelToken((cancel) => {
if (!this.cancelReqMap.has(cancelReqKey)) {
this.cancelReqMap.set(cancelReqKey, cancel);
}
});
}
// 根据当前的请求信息调用对应的取消方法
cancelReq(config) {
const cancelReqKey = this.generateCancelReqKey(config);
if (this.cancelReqMap.has(cancelReqKey)) {
const cancel = this.cancelReqMap.get(cancelReqKey);
cancel(cancelReqKey);
this.removeRequestKey(config);
}
}
// 删除cancelReqMap属性上的值
removeRequestKey(config) {
const cancelReqKey = this.generateCancelReqKey(config);
this.cancelReqMap.delete(cancelReqKey);
}
}
- 引入qs其实是对请求参数进行序列化,
qs.stringify({username: 'admin', password: '123'}) === 'username=admin&password=123'
;- 这里使用了面向对象的方式来封装这个取消请求。因为请求的信息和对应的取消方法要存储起来,方便后面的查找,这里通过
Map
类型的cancelReqMap
属性来存储。这里使用ES6
中的Map
数据结构,是因为它的键不单单局限于字符串,可以是各种数据类型,比普通的对象更加完善。generateCancelReqKey
该方法是根据请求配置信息config
进行拼接,生成由url、method、params、data
组成的字符串。addCancelReqKey
该方法主要是添加请求信息和对应的取消方法到cancelReqMap
属性上。对应的取消方法是需要调用new cancelToken()
才能获取到。cancelReq
该方法主要是根据当前的请求信息调用对应的取消方法,最后把当前的请求信息和取消方法从cancelReqMap
属性上删除。removeRequestKey
根据请求配置项删除cancelReqMap
上的对应的值
完善axios请求封装
import axios from 'axios';
import CancelRequest from './CancelRequest.js';
// 实例化取消请求对象
let cancelRequest = new CancelRequest();
const instance = axios.create({
// ...
});
// 请求拦截器
instance.interceptors.request.use(config => {
// 检查之前是否存在相同的请求,如果存在则取消。
cancelRequest.cancelReq(config);
// 记录当前请求
cancelRequest.addCancelReqKey(config, axios.CancelToken);
return config;
}, err => {
return Promise.reject(err);
});
// 响应拦截器
instance.interceptors.response.use(res => {
// 移除成功请求记录
cancelRequest.removeRequestKey(res.config);
return res.data;
}, err => {
// 移除失败的请求记录
cancelRequest.removeRequestKey(err.config || {});
if (axios.isCancel(err)) {
console.log('重复请求信息:' + err.message);
}
return Promise.reject(err);
});
export default instance;
总结
本文主要介绍了axios
请求的封装和取消重复请求的功能,这里取消的请求,有可能还在请求还没有发送出去的和已经到达服务器被取消的,如果大家对请求拦截的原理感兴趣,可以阅读axios源码解读(下)。
如果读者发现有不妥或者可以改善的地方,欢迎在评论区指出。如果觉得写得不错或者对你有所帮助,可以点赞、评论、转发分享,谢谢~