简介
这是TypeScript
实战的第三篇文章。前面两篇笔者分别介绍了在Vuex和Pinia中怎么使用TypeScript
以及Vuex
和Pinia
的区别。今天我们再用TypeScript
封装一遍Axios
。希望能进一步巩固TypeScript
的基础知识。
Axios几个常用类型
在使用TypeScript
封装Axios
之前我们先来看看Axios
几个重要的类型。
AxiosRequestConfig
AxiosRequestConfig
是我们使用axios
发送请求传递参数的类型。当然它也是我们请求拦截器里面的参数类型。
- axios(config: AxiosRequestConfig)
可以看到,这个config
里面的参数还是挺多的。我们常用的有url、method、params、data、headers、baseURL、timeout
。
- export interface AxiosRequestConfig {
- url?: string;
- method?: Method;
- baseURL?: string;
- transformRequest?: AxiosTransformer | AxiosTransformer[];
- transformResponse?: AxiosTransformer | AxiosTransformer[];
- headers?: any;
- params?: any;
- paramsSerializer?: (params: any) => string;
- data?: any;
- timeout?: number;
- timeoutErrorMessage?: string;
- withCredentials?: boolean;
- adapter?: AxiosAdapter;
- auth?: AxiosBasicCredentials;
- responseType?: ResponseType;
- xsrfCookieName?: string;
- xsrfHeaderName?: string;
- onUploadProgress?: (progressEvent: any) => void;
- onDownloadProgress?: (progressEvent: any) => void;
- maxContentLength?: number;
- validateStatus?: ((status: number) => boolean) | null;
- maxBodyLength?: number;
- maxRedirects?: number;
- socketPath?: string | null;
- httpAgent?: any;
- httpsAgent?: any;
- proxy?: AxiosProxyConfig | false;
- cancelToken?: CancelToken;
- decompress?: boolean;
- transitional?: TransitionalOptions
- }
AxiosInstance
AxiosInstance
是我们使用axios
实例对象类型。
我们使用axios.create(config?: AxiosRequestConfig)
创建出来的对象都是AxiosInstance
类型
- export interface AxiosInstance {
- (config: AxiosRequestConfig): AxiosPromise;
- (url: string, config?: AxiosRequestConfig): AxiosPromise;
- defaults: AxiosRequestConfig;
- interceptors: {
- request: AxiosInterceptorManager<AxiosRequestConfig>;
- response: AxiosInterceptorManager<AxiosResponse>;
- };
- getUri(config?: AxiosRequestConfig): string;
- request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>;
- get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
- delete<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
- head<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
- options<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
- post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
- put<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
- patch<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
- }
可以发现,我们可以使用axios.create、axios.all、axios.spread
方法,但是AxiosInstance
上并没有create、all、spread
等方法,那我们的axios
到底是什么类型呢?
AxiosStatic
- export interface AxiosStatic extends AxiosInstance {
- create(config?: AxiosRequestConfig): AxiosInstance;
- Cancel: CancelStatic;
- CancelToken: CancelTokenStatic;
- isCancel(value: any): boolean;
- all<T>(values: (T | Promise<T>)[]): Promise<T[]>;
- spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R;
- isAxiosError(payload: any): payload is AxiosError;
- }
- declare const axios: AxiosStatic;
可以发现,axios
其实是AxiosStatic
类型,并且继承了AxiosInstance
类型。所以是两者的结合。相较axios.create(config?: AxiosRequestConfig)
创建出来的实例对象,axios
功能是更强大的。
AxiosResponse
AxiosResponse
是非常重要的,我们的axios
请求返回值类型都是AxiosResponse
类型。并且我们可以发现AxiosResponse
是一个接口泛型,这个泛型会应用到后端返回的data
上。所以这块我们可以根据后端接口返回定义不同的类型传递进去。后面笔者在封装常用方法的时候会细说。
- export interface AxiosResponse<T = any> {
- data: T;
- status: number;
- statusText: string;
- headers: any;
- config: AxiosRequestConfig;
- request?: any;
- }
AxiosError
AxiosError
这个类型也是我们必须要知道的。在我们响应拦截器里面的错误就是AxiosError
类型。
- export interface AxiosError<T = any> extends Error {
- config: AxiosRequestConfig;
- code?: string;
- request?: any;
- response?: AxiosResponse<T>;
- isAxiosError: boolean;
- toJSON: () => object;
- }
说完了Axios
的几个常用类型,接下来我们正式开始使用TS
来封装我们的Axios
。
基础封装
首先我们实现一个最基本的版本,实例代码如下:
- // index.ts
- import axios from 'axios'
- import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
-
- class Request {
- // axios 实例
- instance: AxiosInstance
- // 基础配置,url和超时时间
- baseConfig: AxiosRequestConfig = {baseURL: "/api", timeout: 60000}
-
- constructor(config: AxiosRequestConfig) {
- // 使用axios.create创建axios实例
- this.instance = axios.create(Object.assign(this.baseConfig, config))
- }
- // 定义请求方法
- public request(config: AxiosRequestConfig): Promise<AxiosResponse> {
- return this.instance.request(config)
- }
- }
- export default Request
在实际项目中有了基本的请求方法还是远远不够的,我们还需要封装拦截器和一些常用方法。
拦截器封装
拦截器封装只需要在类中对axios.create()
创建的实例调用interceptors
下的两个拦截器即可,
实例代码如下:
- // index.ts
- constructor(config: AxiosRequestConfig) {
- this.instance = axios.create(Object.assign(this.baseConfig, config))
- this.instance.interceptors.request.use(
- (config: AxiosRequestConfig) => {
- // 一般会请求拦截里面加token
- const token = localStorage.getItem("token")
- config.headers["Authorization"] = token;
-
- return config
- },
- (err: any) => {
- return Promise.reject(err)
- },
- )
- this.instance.interceptors.response.use(
- (res: AxiosResponse) => {
- // 直接返回res,当然你也可以只返回res.data
- return res
- },
- (err: any) => {
- // 这里用来处理http常见错误,进行全局提示
- let message = "";
- switch (err.response.status) {
- case 400:
- message = "请求错误(400)";
- break;
- case 401:
- message = "未授权,请重新登录(401)";
- // 这里可以做清空storage并跳转到登录页的操作
- break;
- case 403:
- message = "拒绝访问(403)";
- break;
- case 404:
- message = "请求出错(404)";
- break;
- case 408:
- message = "请求超时(408)";
- break;
- case 500:
- message = "服务器错误(500)";
- break;
- case 501:
- message = "服务未实现(501)";
- break;
- case 502:
- message = "网络错误(502)";
- break;
- case 503:
- message = "服务不可用(503)";
- break;
- case 504:
- message = "网络超时(504)";
- break;
- case 505:
- message = "HTTP版本不受支持(505)";
- break;
- default:
- message = `连接出错(${err.response.status})!`;
- }
- // 这里错误消息可以使用全局弹框展示出来
- // 比如element plus 可以使用 ElMessage
- ElMessage({
- showClose: true,
- message: `${message},请检查网络或联系管理员!`,
- type: "error",
- });
- // 这里是AxiosError类型,所以一般我们只reject我们需要的响应即可
- return Promise.reject(err.response)
- },
- )
- }
在这里我们分别对请求拦截器和响应拦截器做了处理。在请求拦截器我们给请求头添加了token
。
在响应拦截器,我们返回了整个response
对象,当然你也可以只返回后端返回的response.data
,这里可以根据个人喜好来处理。其次对http
错误进行了全局处理。
常用方法封装
在基础封装的时候我们封装了一个request
通用方法,其实我们还可以更具体的封装get、post、put、delete
方法,让我们使用更方便。
并且,我们前面分析到,AxiosResponse
其实是一个泛型接口,他可以接受一个泛型并应用到我们的data
上。所以我们可以在这里再定义一个后端通用返回的数据类型。
比如假设我们某个项目后端接口不管请求成功与失败,返回的结构永远是code、message、results
的话我们可以定义一个这样的数据类型。
- type Result<T> = {
- code: number,
- message: string,
- result: T
- }
然后传递个各个方法:
- public get<T = any>(
- url: string,
- config?: AxiosRequestConfig
- ): Promise<AxiosResponse<Result<T>>> {
- return this.instance.get(url, config);
- }
- public post<T = any>(
- url: string,
- data?: any,
- config?: AxiosRequestConfig
- ): Promise<AxiosResponse<Result<T>>> {
- return this.instance.post(url, data, config);
- }
- public put<T = any>(
- url: string,
- data?: any,
- config?: AxiosRequestConfig
- ): Promise<AxiosResponse<Result<T>>> {
- return this.instance.put(url, data, config);
- }
- public delete<T = any>(
- url: string,
- config?: AxiosRequestConfig
- ): Promise<AxiosResponse<Result<T>>> {
- return this.instance.delete(url, config);
- }
这样当我们调用接口的时候就可以看到我们返回的data
的类型啦。就是我们定义的Result
类型。

所以我们可以直接得到自动提示:

上面调用接口的时候并没有传递接口数据类型,所以我们的result
是any
类型,要想要每个接口都有类型提示,我们还需要给方法传递泛型。
我们再改进下,我们再定义一个login
接口返回值类型loginType
- type loginType = {
- token: string;
- };
然后再调用方法的地方传递进去,然后我们再看看返回值data
的类型。

可以看到他是Result<loginType>
类型,这个loginType
就是result
的类型。
所以我们的result
还可以进一步的得到提示

当然每个接口都定义返回值类型固然好,但是会大大加大前端的工作量。我们在写请求方法的时候也可以不传递接口返回值类型,这样result
的类型就是any
。这个可以根据自身项目需求来选择使用。
看到这小伙伴们是不是都弄懂了呢?如还有疑问欢迎留言。
总结
说了这么多,有些小伙伴们可能有点晕了,下面笔者总结下整个axios
的封装。
- // index.ts
- import axios from "axios";
- import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
- type Result<T> = {
- code: number;
- message: string;
- result: T;
- };
- class Request {
- // axios 实例
- instance: AxiosInstance;
- // 基础配置,url和超时时间
- baseConfig: AxiosRequestConfig = { baseURL: "/api", timeout: 60000 };
-
- constructor(config: AxiosRequestConfig) {
- // 使用axios.create创建axios实例
- this.instance = axios.create(Object.assign(this.baseConfig, config));
-
- this.instance.interceptors.request.use(
- (config: AxiosRequestConfig) => {
- // 一般会请求拦截里面加token
- const token = localStorage.getItem("token");
- config.headers["Authorization"] = token;
-
- return config;
- },
- (err: any) => {
- return Promise.reject(err);
- }
- );
-
- this.instance.interceptors.response.use(
- (res: AxiosResponse) => {
- // 直接返回res,当然你也可以只返回res.data
- return res;
- },
- (err: any) => {
- // 这里用来处理http常见错误,进行全局提示
- let message = "";
- switch (err.response.status) {
- case 400:
- message = "请求错误(400)";
- break;
- case 401:
- message = "未授权,请重新登录(401)";
- // 这里可以做清空storage并跳转到登录页的操作
- break;
- case 403:
- message = "拒绝访问(403)";
- break;
- case 404:
- message = "请求出错(404)";
- break;
- case 408:
- message = "请求超时(408)";
- break;
- case 500:
- message = "服务器错误(500)";
- break;
- case 501:
- message = "服务未实现(501)";
- break;
- case 502:
- message = "网络错误(502)";
- break;
- case 503:
- message = "服务不可用(503)";
- break;
- case 504:
- message = "网络超时(504)";
- break;
- case 505:
- message = "HTTP版本不受支持(505)";
- break;
- default:
- message = `连接出错(${err.response.status})!`;
- }
- // 这里错误消息可以使用全局弹框展示出来
- // 比如element plus 可以使用 ElMessage
- ElMessage({
- showClose: true,
- message: `${message},请检查网络或联系管理员!`,
- type: "error",
- });
- // 这里是AxiosError类型,所以一般我们只reject我们需要的响应即可
- return Promise.reject(err.response);
- }
- );
- }
-
- // 定义请求方法
- public request(config: AxiosRequestConfig): Promise<AxiosResponse> {
- return this.instance.request(config);
- }
-
- public get<T = any>(
- url: string,
- config?: AxiosRequestConfig
- ): Promise<AxiosResponse<Result<T>>> {
- return this.instance.get(url, config);
- }
- public post<T = any>(
- url: string,
- data?: any,
- config?: AxiosRequestConfig
- ): Promise<AxiosResponse<Result<T>>> {
- return this.instance.post(url, data, config);
- }
- public put<T = any>(
- url: string,
- data?: any,
- config?: AxiosRequestConfig
- ): Promise<AxiosResponse<Result<T>>> {
- return this.instance.put(url, data, config);
- }
-
- public delete<T = any>(
- url: string,
- config?: AxiosRequestConfig
- ): Promise<AxiosResponse<Result<T>>> {
- return this.instance.delete(url, config);
- }
- }
- export default Request;
到此这篇关于TypeScript利用TS封装Axios实战的文章就介绍到这了,更多相关TypeScript封装Axios内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!