写在前面
Axios 是前端最常用的请求库,提供的功能也非常完善,能满足我们日常开发中的请求需要,但我们通常在项目里面都不会直接使用 Axios 库。
我认为至少有两方面原因。首先,将项目中通用的请求逻辑进行封装,可以避免 DRY(不要重复自己) ,常见的通用逻辑包括请求拦截、响应拦截,错误处理,请求取消、以及配置或数据格式统一处理等。其次,对 Axios 库进行封装可以统一管理请求,提升了后续开发的可维护性。
每个项目具体的业务场景以及后端接口不一致,会导致对api.js
的封装有细微的差别,但通过我多年的开发经验来看,将API封装成类的形式是最佳实践。
下面让我们按照请求流程,从零开始封装属于自己的Axios请求。
封装Api类
首先,我们从创建一个最简单的 Api 类开始,分别定义 request、get、post、put 等请求方法,其中 _createApi
为私有方法用于根据配置创建 Axios
请求实例,并添加请求拦截器和响应拦截器。
下面是 config.js
文件中的默认配置。
请求参数处理
有的时候,我们传递的参数并不是完全符合 Axios
的配置格式,那我们就需要在 Axios
创建请求之前将参数处理成统一格式或者是添加一下额外的配置,比如 skipErrorHandler
。
具体实现就是,在我们在创建 _createApi
的时候, 使用 transformRequestConfig
方法将参数处理成 Axios
的配置格式。
请求拦截器
请求拦截器中可以做很多事情,比如修改请求头,请求取消等。修改请求头比较简单,下面主要讲讲怎么取消请求。
取消请求
在切换页面后,取消之前还未完成的axios请求,可以避免之前的请求结果影响当前页面的判断并减少不必要的请求消耗。
实现思路就是将当前页面的所以请求取消方法存在state中,然后在router.beforeEach()钩子函数中遍历执行所有的取消方法。
首先,我们在 store 中定义一个 cancelTokenArr
用于存储当前页面的所有的取消请求方法。接着定义 ADD_CANCEL_TOKEN
和 CLEAR_CANCEL_TOKEN
分别用于向数组 cancelTokenArr
中添加取消方法 和 通过遍历数组 cancelTokenArr
来取消当前页面的所有请求。
然后,我们就可以在请求拦截器中添加 addCancelToken
,用于在每次发起请求的时候,将当前请求的取消方法添加到数组中。
最后,我们就可以在 router.beforeEach
钩子中,监听页面的切换,从而取消当前页面的所有请求。一定要注意在最后执行next()
方法,否则路由不会继续执行。
需要注意的是,取消请求会出现 error 错误,如果我们不希望报错,则可以通过 axios.isCancel
来判断当前请求是否是被取消的,来进行一些特殊的处理。
响应拦截器
响应拦截器中,通常是进行错误处理和数据处理。
错误处理
常见的错误,包括网络错误、系统错误和业务错误。通常的做法是将这三种错误全部放在响应拦截器中进行处理,并且使用 switch-case
的语法来处理不同的状态码。
如果错误处理不多,这样完全没有问题,但一旦错误状态过多,就会导致响应拦截器中显得臃肿,后续的可维护性就变得极其差。
为了解决这个问题,首先,我们可以按照错误类型进行分类处理,将不同类型错误处理拆分成不同的函数,分别处理不同的错误类型。其次,每种错误类型处理我们尽可能用==程序逻辑==去替代人脑逻辑,通常的做法就是使用Map数据结构,这是一种非常常用的做法,需要逻辑判断的地方都能使用Map数据结构进行处理。
上面的 handleBusinessError
和 handleGeneralError
处理方法都大同小异,通过 errCode
获取到当前错误,然后就可以进行错误处理或者提示。
如果部分请求需要忽略拦截器的全局错误处理,通常可以在config中传递skipErrorHandler
的方式进行处理。
数据处理
接口返回的数据有时候并不是我们真正想要的数据结构或者存在数据层级嵌套会比较深的情况。这样就可以通过 transformResponseData
将数据处理成标准的结构之后,才返回给业务层使用,这样会极大的减少业务中大量的冗余代码。
如果要获取接口数据中比较深层次的数据,这里可以使用 Lodash
的 get
,通过传递对象属性路径获取到对应的属性。
如何使用
我们将Api 挂载到Vue.prototype
上,这样就可以很方便的在全局进行访问。
经过上面一系列的封装,在业务中使用请求就非常简单了,只需要将 await this.$Api.request(params)
放入 try-catch
块中,正常的代码逻辑按顺序执行,错误逻辑则可以在catch
块中进行处理。
完整代码
下面是api.js
的完整代码,这里只是为大家提供一些自己在项目中封装 Axios
的一些思想方法,每个项目的具体需求可能不太一样,很多地方不能做到开箱即用,而是需要根据自己的项目进行调整。
这次封装中最为重要的一点就是将请求封装成Api类和使用了单一职责的思想方法,也就是在封装的过程中尽量将每个功能点都拆分成方法。我想在这种思想方法的框架下,其余的都是小问题了。
当然,除了上面提到的一些常见的封装,大家也可以在此基础上根据业务需求进行不断迭代。
扩展阅读