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 | { |
通过 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)
v1.5.2