一、axios的实例与请求流程
下图是axios实例属性的简图。
下图是axios的请求流程
axios的实例上,其实主要就这三个东西:
- config:配置,比如url、method、params、headers等等
- interceptors :拦截器,分为请求拦截器和返回拦截器。
- request:调用xhr或者http请求的方法,参数就是config
二、CancelToken 的作用
Axios 是一个基于 Promise 的 HTTP 客户端,用于在浏览器和 Node.js 中发起 HTTP 请求。除了强大的功能和丰富的配置选项,Axios 还提供了一个实用的特性——CancelToken,它允许我们取消一个或多个正在进行的请求。
在网络应用中,我们经常需要处理一些可能需要取消的请求,比如用户在加载数据时跳转到了其他页面,或者在表单提交前用户点击了取消按钮。在这些场景下,如果能够取消请求,就能避免不必要的资源消耗和更好的用户体验。
三、CancelToken 的实现原理
CancelToken 的工作原理依赖于 Promise 的链式调用和 throw 语句。在 Axios 发起请求时,会检查请求的 cancelToken 属性,如果存在,就将其添加到当前请求的 cancelToken 列表中。
当调用 cancel 方法时,CancelToken 会抛出一个带有特定 message 的 Cancel 对象。这个对象会被 Promise 的 catch 方法捕获,从而触发请求的取消逻辑。
四、取消请求的流程
CancelToken的静态方法CancelToken.source返回了两个东西:分别是token和cancel方法。
下图是取消请求的流程
可以看到,流程里,axios分别在请求前、请求中、请求后进行了是否取消的判断。
这也就意味着,当我们在请求拦截器中调用cancel方法,axios会识别到,抛出异常。
五、CancelToken用法
先引用axios.CancelToken,然后调用source()方法,会产生一个token和cancel
const CancelToken = axios.CancelToken; const source = CancelToken.source(); // get请求示例 axios.get('/user/12345', { cancelToken: source.token }).catch((thrown)=> { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // 处理错误 } }); // post请求示例 axios.post('/user/12345', {name: 'new name'}, {cancelToken: source.token}); // 取消请求(message 参数是可选的) source.cancel('不想请求了');
- get请求的时候,cancelToken是放在第二个参数里;
- post的时候,cancelToken是放在第三个参数里。
六、利用拦截器取消请求
先封装一下方法
// 全局变量对象,专门存放所有请求的cancel方法 window.pendingRequest = new Map() // 生成request的唯一的标识 const generateRequestKey = (config = {}) => { // 通过url,method,params,data生成唯一key,用于判断是否重复请求 // params为get请求参数,data为post请求参数 const {url,method,params,data} = config if (method == 'get') { return [url, method, qs.stringify(params)].join('&') } return [url, method, qs.stringify(data)].join('&') } // 将重复请求添加到pendingRequest中 const addPendingRequest = (config) => { const key = generateRequestKey(config) if (!pendingRequest.has(key)) { config.cancelToken = new axios.CancelToken(cancel => { pendingRequest.set(key, cancel) }) } } // 取消重复请求 const cancelRepeatRequest = (config) => { const key = generateRequestKey(config) if (pendingRequest.has(key)) { const cancelToken = pendingRequest.get(key) cancelToken(key) // 取消之前发送的请求 pendingRequest.delete(key) // 请求对象中删除requestKey } }
1、axios请求拦截器
在发送请求之前,为每个请求生成一个唯一的标识,这个标识可以根据请求的URL、方法以及所传递的参数等信息生成。
通过使用Axios的请求拦截器,你可以在请求发送前进行检查,如果发现有一个相同标识的请求已经在进行中,就可以使用CancelToken取消前一个请求,或者跳过当前请求。
// 创建axios实例 const httpService = axios.create({}); // request拦截器 httpService.interceptors.request.use( config => { // 取消重复请求 cancelRepeatRequest(config) // 添加请求到队列 addPendingRequest(config) return config; }, error => { return Promise.reject(error); } )
2、axios响应拦截器
响应拦截器可以用来清理已完成或已取消的请求的记录,确保请求映射表只保留活跃的请求。这个机制使得在实际应用中管理和取消重复的请求变得容易。
httpService.interceptors.response.use( response => { // 取消请求队列中该请求 cancelRepeatRequest(response .config); if (response.status == 200) { return response; } else { return Promise.reject(response); } }, // 处理处理 error => { // 取消请求队列中该请求 cancelRepeatRequest(error.config || {}) return Promise.reject(error); } )
这里我同时发送了3次重复的history请求,过滤取消了2次,成功发送了1次。
3、利用路由导航守卫取消请求
当路由切换的时候,在路由钩子里遍历这个数组,调用所有的cancel方法,将上一个页面还未完的请求取消,这样就可以加快页面的加载,避免不必要的等待。
router.beforeEach((to, from, next) => { if(pendingRequest){ pendingRequest.forEach((cancel) => { cancel(); }); pendingRequest = new Map(); } next(); })
当路由切换页面的时候,遍历全局数组,将上一个页面的所有请求cancel掉。
图中history接口是上个页面的,还没返回结果,切换路由后,请求被取消。