1. 需求及引言
项目中用到axios作为HTTP请求库,为了考虑到性能与用户的体验,以及一些特殊情况(例如:数据量大导致响应时间慢,用户网速慢,用户双击等等),因此需要加上防抖来避免多次重复请求。
注:以下方法仅适用于全局控制所有接口防抖,并无法控制防抖的时间,仅能控制单个接口在未响应前无法重复发出请求
2. 解决方案
想要统一控制状态,就需要用到请求拦截器和响应拦截器
先写工具函数
// axios 增加防抖
// 正在进行中的请求列表
let reqList = [];
/**
* 阻止重复请求
* @param {array} reqList - 请求缓存列表
* @param {string} url - 当前请求地址
*/
const isRepeatRequest = (reqList, url) => {
if (reqList.includes(url)) {
return true;
}
return false;
};
// 请求成功删掉队列中的请求
const removeFinishedRequest = (reqList, url) => {
reqList = reqList.filter(item => item !== url);
return reqList;
};
// 生成用于对比的url url + method + data + params
const getCheckRepeatRequestUrl = (url, method, data, params) => {
return `${url}&&&${method}&&&${JSON.stringify(data)}&&&${JSON.stringify(params)}`;
};
在axios请求和响应拦截器中,控制防抖
// 请求拦截器
service.interceptors.request.use(
config => {
// 这里我只是简单的做了 url 的单一识别
if (!stopRepeatRequest(reqList, getCheckRepeatRequestUrl(service.$host + config.url, config.method, config.data, config.params))) {
// 如果这个请求时重复的,就直接 reject
return Promise.reject({
repeatRequest: true
});
}
// 将此次请求的 url 加入到请求队列中
reqList.push(getCheckRepeatRequestUrl(service.$host + config.url, config.method, config.data, config.params));
return config;
},
error => {
// 请求失败时的code
}
);
service.interceptors.response.use({
//请求成功
res => {
// 请求成功后,将此次请求的 url 从队列中清除
reqList = removeFinishedRequest(
reqList,
getCheckRepeatRequestUrl(res.config.url, res.config.method, res.config.data, res.config.params)
);
return res;
},
error => {
// 如果 error 有 response 表示此接口有请求
if (error.response) {
// 就将此请求删除掉,因为接口会出现报错(401,500等等),这也算是请求了,并且不是重复请求
// 所以在这里,错误的请求也要从队列中删除掉
reqList = removeFinishedRequest(
reqList,
getCheckRepeatRequestUrl(
err.response.config.url,
err.response.config.method,
err.response.config.data,
err.response.config.params
)
);
} else {
// 没有 response 说明是我们手动 rejcet 掉的请求,就直接 return 就行了
return error;
}
}
})
这样就实现了一个简单的请求防抖
3. 警告
- 该方法只是一种非常简单的请求防抖方法,大型项目生产环境请勿随意使用。
axios官方提供了cancelToken的方式来拦截请求,大型复杂项目可以采用这种方式