在开发中,常常会遇到接口重复请求致使的各类问题。vue
对于重复的get
请求,会致使页面更新屡次,发生页面抖动的现象,影响用户体验。node
对于重复的post
请求,会致使在服务端生成两次记录(例如生成两条订单记录)。react
若是当前页面请求还未响应完成,就切换到了下一个路由,那么这些请求直到响应返回才会停止。ios
不管从用户体验或者从业务严谨方面来讲,取消无用的请求确实是须要避免的。git
固然咱们能够经过页面loading
来避免用户进行下一次的操做,但本文只讨论单纯的如何取消这些无用的请求。es6
axios
是一个主流的http
请求库,它提供了两种取消请求的方式。github
经过axios.CancelToken.source
生成取消令牌token
和取消方法cancel
chrome
const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(function(thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error } }); axios.post('/user/12345', { name: 'new name' }, { cancelToken: source.token }) // cancel the request (the message parameter is optional) source.cancel('Operation canceled by the user.');
经过axios.CancelToken
构造函数生成取消函数vue-cli
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // An executor function receives a cancel function as a parameter cancel = c; }) }); // cancel the request cancel();
须要注意的是在catch
中捕获异常时,应该使用axios.isCancel()
判断当前请求是不是主动取消的,以此来区分普通的异常逻辑。
上面有两种取消请求,用哪一种都是能够的,这里使用第二种。element-ui
取消请求主要有两个场景:
method
,请求路径url
,请求参数(get
为params
,post
为data
)都相同时,能够视为同一个请求发送了屡次,须要取消以前的请求咱们封装几个方法:
// 声明一个 Map 用于存储每一个请求的标识 和 取消函数 const pending = new Map() /** * 添加请求 * @param {Object} config */ const addPending = (config) => { const url = [ config.method, config.url, qs.stringify(config.params), qs.stringify(config.data) ].join('&') config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => { if (!pending.has(url)) { // 若是 pending 中不存在当前请求,则添加进去 pending.set(url, cancel) } }) } /** * 移除请求 * @param {Object} config */ const removePending = (config) => { const url = [ config.method, config.url, qs.stringify(config.params), qs.stringify(config.data) ].join('&') if (pending.has(url)) { // 若是在 pending 中存在当前请求标识,须要取消当前请求,而且移除 const cancel = pending.get(url) cancel(url) pending.delete(url) } } /** * 清空 pending 中的请求(在路由跳转时调用) */ export const clearPending = () => { for (const [url, cancel] of pending) { cancel(url) } pending.clear() }
Map
是ES6
中一种新型的数据结构,自己提供了诸多方法,方便操做,适合当前场景。若是不熟悉的能够查看ECMAScript 6 入门。
在给config.cancelToken
赋值的时候,须要判断当前请求是否已经在业务代码中使用了cancelToken
qs
是一个专门用来转换对象和字符串参数的库,最初是由 TJ 建立并维护的,也是axios
推荐使用的参数序列化库。这里咱们的目的只是单纯的将参数对象转换为字符串方便拼接。
Map
结构默认部署了Symbol.iterator
属性,可使用for...of
循环直接获取键名和键值,固然你也可使用for...in
循环。
主要的方法已经写好了,只须要添加到axios
拦截器中就能够了。
axios.interceptors.request.use(config => { removePending(options) // 在请求开始前,对以前的请求作检查取消操做 addPending(options) // 将当前请求添加到 pending 中 // other code before request return config }, error => { return Promise.reject(error) }) axios.interceptors.response.use(response => { removePending(response) // 在请求结束后,移除本次请求 return response }, error => { if (axios.isCancel(error)) { console.log('repeated request: ' + error.message) } else { // handle error code } return Promise.reject(error) })
将clearPending()
方法添加到vue
路由钩子函数中
router.beforeEach((to, from, next) => { clearPending() // ... next() })
最后咱们能够在浏览器中测试下,能够将chrome
中控制面板的Network
的网络状态切换为Slow 3G
来模拟网速慢的状况。
咱们把查询按钮的loading
或者disabled
属性干掉来方便测试
在上面控制面板中能够看到,红色的status
为canceled
的就是被取消的请求。
上面代码在e-admin-vue(一个使用 vue + element-ui + vue-cli3 构建的 rbac 权限模型)或者e-admin-react(一个使用 react + antd + create-react-app 构建的 rbac 权限模型)中都有体现,欢迎 star。