Axios作为Vue官方推荐的HTTP请求库,甚至取代了原有的vue-resource,在我第一回接触到axios时,我就在想这个库之所以被官方推荐,和其他同类库相比肯定是有一些特别的地方。
概述
在这篇文章里我将会举例说明我所使用过的axios一些特性的使用场景,更多的使用方法可以参考官方文档,本文涉及的特性如下:
- Promise API
- 请求合并
- 拦截器
- 请求取消
基本用法
第一回使用axios时,我的感觉是“这和requests库也太像了”,但实际上axios的写法有几种。
1 | // 将请求方式写进选项里 |
有些选项是全局通用的,每次请求时都进行局部设置将会不利于维护,axios也提供了修改全局选项的途径,要注意的是全局选项的优先级要低于局部选项。
1 | // 通过create使用给定的选项创建实例,像timeout,baseURL这种设置通常是全局通用的,不必在每次请求的时候都重新设置 |
Promise API
axios的请求操作都是使用Promise进行封装的,避免在执行先后操作时造成的“回调地狱”,同时也就意味着可以使用ES7的async/await语法进行修饰。
1 | // 比如说我要从第一个响应里取得id属性,将id的值放入第二个请求中,这是对执行顺序敏感的操作 |
请求合并
上一节的需求是按顺序执行操作,还有一种情况是要求从多个接口获得数据,将数据组合后再使用。
比如说从category接口得到所有的目录,从news接口获得所有的新闻,根据新闻中的目录id,将新闻分类放到不同的目录下。
1 | // 这部分是错误案例 |
首先,合并请求的意思是同时发起多个请求,在所有请求都响应后才执行then的操作,并不是将多个请求合成为一个。
正确案例的写法是官方提供的,可以拆成两部分来看,实际上负责合并与收集结果的是all函数,如果不使用spread函数,在then中能得到一个由所有响应构成的列表。
那么spread函数的用途就显而易见了,他是一个高阶函数,将列表中的内容作为参数依次传递给wrapped函数,简单来说就是把列表展开成回调函数参数,供开发者后续操作时使用的,可参考源码中的注释。
如果对Promise比较熟悉的话,看到all函数就会想到Promise中的all,事实上axios.all就是Promise.all,参考源码,所以直接使用Promise.all也是有效的:
1 | Promise.all([req1, req2]).then() |
拦截器
拦截器是很重要的一个特性,他在HTTP请求的过程中提供了两个注入点,一个是发送请求前,另一个是接收响应后(还未执行后续操作)。
请求前
先说请求前这个注入点,在前后端分离的开发中,后端会提供API供前端使用,那么如何验证用户的身份呢?常用的方式有Cookies-Session,JWT等,主要是这个JWT,它将Token交给前端维护,每次访问需要授权的接口都得带上Token来表明自己的身份,由于这个Token是动态的,并且无法在项目初始化时获取,所以不能将它写入全局选项里。
得先通过登陆之类的接口取得Token,之后在发送其他请求前将Token带入使用,这里就得用到请求前这个注入点了。
响应后
响应后这个注入点就比较关键了,接口的结构是固定的,比如:1
2
3
4
5{
code: Number,
message: String,
data: Object
}
通过code来判断用户的请求是否执行成功,虽然每个接口成功与不成功的后续操作不一样,但总是能找出通用地方,比如错误原因为“未授权”的时候,要跳转到登录页面,或者发生错误时弹出提示通知用户。
如果在每一次的响应都单独处理一遍这种情况,那肯定不利于维护,所以可以通过响应后这个注入点来做统一处理。
样例
1 | axios.interceptors.request.use((config) => { |
请求取消
之所以把这个特性放在最后,是因为它可能比较难理解,举个自动补全的例子,在搜索时打字,搜索框会给你补全剩余的内容,当然,不一定是每打一个字他都去请求补全结果的,这部分通常会使用去抖函数,但是用了去抖函数也会遇到一种情况:
发起请求1 -> 发起请求2 -> 等待 -> 接收响应2 -> 接收响应1
请求2完成的速度比请求1要快,好比说搜索一个“你好”,用户看到的补全是“你”的内容,在请求1和2之间增加延迟能降低这种问题发生的概率,但是同时也会拉低用户体验。
所以为了有效解决这个问题,可以使用axios提供的请求取消特性。
1 | // 取得一个Cancel Token |
CancelToken是一个构造函数,CancelToken.source()
创建了一个source实例,结构为{ token, cancel }
,每一次调用CancelToken.source()
,都会生成一个新的token和对应的cancel函数。
由于生成的token和cancel是相对应的,所以使用token标记请求后,调用对应的cancel就能取消请求,在请求被取消之后会直接进入异常捕获环节,要区分主动取消的请求和异常可以使用isCancel
函数。
根据源码可以得知,在调用cancel
取消请求后会创建Cancel
对象作为参数resolve promise,Cancel
对象中会设置__CANCEL__
属性作为flag,isCancel
函数就是通过这个属性来判断请求是否是被主动取消的。
(请求的取消会使CancelToken
的Promise resolve,请求前如果发现请求要取消,会将请求的Promise reject)