学习 coderwhy 老师结合 ts 二次封装 axios
目录结构
config
config\index.ts
// export const BASE_URL = "http://codercba.com:9002";
export const TIME_OUT = 10000;
// 1. 根据环境变量区分接口地址
// let BASE_URL: string;
// if (process.env.NODE_ENV === "development") {
// BASE_URL = "http://codercba.com:9002"
// } else {
// BASE_URL = "http://codercba.com:9002"
// }
// 2. 通过创建 .env 文件来自定义环境变量
const BASE_URL = process.env.REACT_APP_BASE_URL
export { BASE_URL }
request
request\index.ts
import axios, {AxiosInstance, InternalAxiosRequestConfig} from "axios";
import { RequestConfig } from "@/service/request/type";
class MyRequest {
instance: AxiosInstance;
constructor(config: RequestConfig) {
this.instance = axios.create(config);
// 1. 全局拦截器和实例拦截器
this.instance.interceptors.request.use(
function (config) {
console.log("全局请求成功的拦截");
return config;
},
function (error) {
console.log("全局请求失败的拦截");
return Promise.reject(error);
}
);
this.instance.interceptors.response.use(
function (response) {
console.log("全局响应成功的拦截");
return response.data;
},
function (error) {
console.log("全局响应失败的拦截");
return Promise.reject(error);
}
);
// 2. 配置针对特殊的接口的单次请求拦截
this.instance.interceptors.request.use(
config.interceptors?.requestSuccessFn,
config.interceptors?.requestFailureFn
);
this.instance.interceptors.response.use(
config.interceptors?.responseSuccessFn,
config.interceptors?.responseFailureFn
);
}
/*
* 我们希望对每次请求每个接口 request 和 response 都进行定制化的拦截
* request({
* url:'/xxx',
* interceptors:{
* requestSuccessFn:(config) => {
* console.log("针对 /xxx 请求成功的拦截");
* return config;
* },
* }
* })
*
* 某个接口的请求拦截 -> 全局请求拦截 -> 全局响应拦截 -> 某个接口的响应拦截
* */
/**
* 封装请求方法
* @param config
*/
request<T = any>(config: RequestConfig<T>) {
if (config.interceptors?.requestSuccessFn) {
// 返回拦截处理后新的 config
// 如今新的源码里面需要使用 InternalAxiosRequestConfig 否则会报错
config = config.interceptors.requestSuccessFn(config as InternalAxiosRequestConfig) as InternalAxiosRequestConfig;
}
return new Promise<T>((resolve, reject) => {
this.instance
.request<any, T>(config)
.then((res) => {
if (config.interceptors?.responseSuccessFn) {
res = config.interceptors.responseSuccessFn(res);
}
resolve(res);
})
.catch((err) => reject(err));
});
}
get<T = any>(config: RequestConfig<T>) {
return this.request({ ...config, method: "GET" });
}
post<T = any>(config: RequestConfig<T>) {
return this.request<T>({ ...config, method: "POST" });
}
delete<T = any>(config: RequestConfig<T>) {
return this.request<T>({ ...config, method: "DELETE" });
}
patch<T = any>(config: RequestConfig<T>) {
return this.request<T>({ ...config, method: "PATCH" });
}
}
export default MyRequest;
request\type.ts
import {AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig} from "axios";
/**
* 自定义拦截器类型
*/
export interface Interceptors<T = AxiosResponse> {
requestSuccessFn?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;
requestFailureFn?: (err: any) => any;
responseSuccessFn?: (res: T) => T;
responseFailureFn?: (err: any) => any;
}
/**
* 针对于原有 axios 的配置进行二次封装(扩展)
*/
export interface RequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
interceptors?: Interceptors<T>;
}
index
index.ts
import {BASE_URL, TIME_OUT} from "@/service/config";
import MyRequest from "@/service/request";
export const request = new MyRequest({
baseURL: BASE_URL,
timeout: TIME_OUT
})
总结
- 配置的统一管理 .env 比如 timeout 和 baseUrl
- 拦截器二次封装(接口(通过 ts 类型约束和类的继承为每一个 request 方法添加自定义 interceptors 配置,配置上有请求和响应成功和失败的方法)、实例(类构造实例)、全局)
- 全局拦截(token 设置、loading 效果、message 弹窗提示)
整体下来最难的地方我感觉是 ts 类型的约束,不看一些源码真的理解不了。