借用掘友(橙某人)的前言解释: 提及这个重复请求,感受用到得比较少,心理总有怎么一种想法就算多请求一次又能怎么样,服务器会塌吗?页面会挂吗?明显不会嘛,不要大惊小怪,哈哈哈。再说没事怎么会多发重复的请求呢?不可能的。前端
并且作取消重复请求操做,其实取消后的请求仍是有可能会到达了后端,只是前端浏览器不处理而已,可是呢,哎,咱们仍是得作作工做,不,非作不可,所谓以防万一,严谨,程序猿须要严谨!!!vue
发生重复请求的场景通常有这两个:ios
<keep-alive />
。正言:axios
首先咱们要收集请求中的接口并判断哪些请求是重复请求,咱们才能取消它,那么如何判断呢?很简单,只要是请求地址、请求方式、请求参数同样,那么咱们就能认为是同样的。而咱们要存储的队列里面的数据结构很明显应该是以键值对的形式来存储,这里面咱们选择 Map
对象来操做。后端
// axios.js
const pendingMap = new Map();
/**
* 生成每一个请求惟一的键
* @param {*} config
* @returns string
*/
function getPendingKey(config) {
let {url, method, params, data} = config;
if(typeof data === 'string') data = JSON.parse(data); // response里面返回的config.data是个字符串对象
return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&');
}
/**
* 储存每一个请求惟一值, 也就是cancel()方法, 用于取消请求
* @param {*} config
*/
function addPending(config) {
const pendingKey = getPendingKey(config);
config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {
if (!pendingMap.has(pendingKey)) {
pendingMap.set(pendingKey, cancel);
}
});
}
复制代码
// axios.js
/**
* 删除重复的请求
* @param {*} config
*/
function removePending(config) {
const pendingKey = getPendingKey(config);
if (pendingMap.has(pendingKey)) {
const cancelToken = pendingMap.get(pendingKey);
cancelToken(pendingKey);
pendingMap.delete(pendingKey);
}
}
复制代码
// axios.js
function myAxios(axiosConfig) {
const service = axios.create({
baseURL: 'http://localhost:8888', // 设置统一的请求前缀
timeout: 10000, // 设置统一的超时时长
});
service.interceptors.request.use(
config => {
removePending(config);
addPending(config);
return config;
},
error => {
return Promise.reject(error);
}
);
service.interceptors.response.use(
response => {
removePending(response.config);
return response;
},
error => {
error.config && removePending(error.config);
return Promise.reject(error);
}
);
return service(axiosConfig)
}
复制代码
咱们在上面提到的 getList()
方法里面简单模拟连续发出了三次重复请求,而后把浏览器设置成3G模式,就能看到效果啦。浏览器
// App.vue
function getList() {
getListAPI().then(res => {console.log(res)})
setTimeout(() => {
getListAPI().then(res => {console.log(res)})
}, 200);
setTimeout(() => {
getListAPI().then(res => {console.log(res)})
}, 400);
}
复制代码
须要注意,上面说了取消正在请求中的接口,说明这接口有可能已经到达后端了,只是后端响应慢,因此若是你的接口响应比较快的话,就很难看到效果;若是你是本身搭建的服务,只要经过接口返回时延时下就能够看到效果;又或者经过浏览器的network调整网络速度也能够哦。
缓存
对于取消后的请求咱们也应该有个合理的处理,不能就无论了,尽量的达到代码可控的底部,它会被归类到异常里面,下面会说到( ^ω^ )。服务器
之因此弄成配置化取消重复请求,是由于可能存在一些特殊变态的场景状况,是须要重复请求,如输入实时搜索、实时更新数据等,反正就是可能存在吧。( ̄y▽ ̄)~*markdown
// request interceptor
service.interceptors.request.use(
config => {
if (config.method === 'upload') {
config = {
...config,
...{
method: 'post',
data: config.data,
methodType: 'upload',
headers: {
'Content-Type': 'multipart/form-data' // 须要指定上传的方式
},
transformRequest: [function() {
return config.data
}]
}
}
}
removePending(config)
config.repeat_request_cancel && addPending(config)
return config
},
error => {
// do something with request error
console.log('error', error) // for debug
return Promise.reject(error)
}
)
复制代码
咱们在上面增长了一个自定义配置的参数,如今每一个API方法就能拥有两个参数,第一个参数传递的是axios本来的一些配置,第二个参数就是咱们本身的一些自定义参数了,如咱们定义 repeat_request_cancel
来控制是否开启取消重复请求的功能。后续更多功能,咱们也能添加进其中,至关于可定制化每一个API方法,是否是很棒!!!网络
// 营销信息管理-列表
// 配置repeat_request_cancel的Boolean类型来判断
export function getProductElementInfoUrlByReq(data, loading = true) {
return request({
url: '/xxxxx/xxxxxxx',
method: 'post',
data: data,
loading,
repeat_request_cancel: true
})
}
复制代码
over!