TypeScript封装axios
简介
最近在用Vue3 + TypeScript 重构一个Vue2项目,之前项目中用到axios来发送网络请求,进行前后端交互,但并未对axios库做过多的封装,导致代码重复度较高,维护起来比较麻烦,乘此机会对axios进行一次较为完整的封装,这里我考虑用面向对象的思想来进行实践。
1. 认识axios
1.1 为什么选择axios
选择axios库有两个原因:
- axios是Vue给官方推荐的网络请求库,尤雨溪16年有在微博上公告;
- axios库功能强大,具备以下功能特点;
- 在浏览器中发送XMLHttpRequests请求
- 在nodejs中发送http请求
- 支持Promise API
- 拦截请求和响应
- 转换请求和响应数据
- 等等
2. axios基本使用
因axios基础使用十分简单,可参考axios官方文档 https://axios-http.com/,这里不在啰嗦axios的其他基本用法,主要说说拦截器。
拦截器主要分为两种,请求拦截器和响应拦截器。
请求拦截器:请求发送之前进行拦截,应用于我们在请求发送前需要对请求数据做一些处理。例如:
- 携带token
- 当请求时间过长时,设置loading
响应拦截器:在响应到达时进行拦截,应用于在我们业务代码中拿到数据之前,需要对数据做一定处理。例如: - 转换数据格式
- 移除loading
3. 用面向对象的思想封装axios
3.1 为什么要对axios进行封装?
在项目中会有很多的模块都需要发送网络请求,常见的比如登录模块,首页模块等,如果我们项目中直接使用诸如axios.get(), axios.post(),会存在很多弊端,哪些弊端呢?
- 首先这样做会导致我们每个模块对axios依赖性太强,意味着我们项目中的每个模块都和一个第三方库耦合度较高,这样的话,如果axios不在维护,我们要更换库的时候将非常麻烦,我们可以假设一下,随着时间的推移,axios可能因为浏览器的升级,Webpack的改变而出现一些bug, 然而axios已不再维护,这时我们往往需要切换库,这就意味着我们需要去修改每个模块中的请求相关的代码,显而易见,非常繁琐。
- 还有一点,在我们发送网络请求的时候,往往会有很多共同的特性,比如说,在我们成功登录之后的其他请求中,我们往往需要在请求头中添加token,然后发送请求;在每次请求中,我们想展示一个loading… 这些功能如果在每次请求的逻辑中都写一遍,很明显,我们的代码重复度太高了。
封装带来的好处:
- 解决以上弊端,降低与第三方库的耦合度,这样我们将来需要更换库时,只需要修改我们封装后的request即可,这样我们往往只是修改封装后一两个文件,而不再需要每个模块每个模块的修改。
在我们开发中,并不一定会用这种思想来封装axios,对get,post,delete等请求方法分别封装然后分别导出封装后的函数也很常见,但我认为class的相关语法封装性会更好,因此这里我选择尝试用类相关的概念来封装axios.
我想要的封装后达到的效果:可以直接在其他项目使用。
3.2 用面向对象的思想封装axios
3.2.1 基础封装
创建一个request.ts文件,导入axios库,由于axios实例,请求需要传入的数据以及响应返回的数据都有各自定义好的类型,因此我们除了导入axios类之外,还需导入所需的类型接口。
从axios源码中可知,axios实例的类型为AxiosInstance,响应的数据类型为AxiosResponse,请求需要传入的参数类型为AxiosRequestConfig
// request.ts
import axios, {
AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
// 自定义请求返回数据的类型
interface HData<T> {
data: T;
returnCode: string;
success: boolean;
}
class HRequest {
config: AxiosRequestConfig;
instance: AxiosInstance;
constructor(options: AxiosRequestConfig ) {
this.config = options;
this.instance = axios.create(options);
}
// 类型参数的作用,T决定AxiosResponse实例中data的类型
request<T = any>(config: AxiosRequestConfig ): Promise<T> {
return new Promise((resolve, reject) =>