源码github连接:baojinghui/MyAxios (github.com)
1,为什么要封装axios ,因为当一个库不维护时,便于在封装的地方把不维护的库替换掉
2,因为axios返回的是一个实例对象,所以只能这一个实例上修改。但是我们的接口有两种baseUrl,就可以通过封装,不同的接口创建新的实例去配置,不同实例互不干扰。
3,配置拦截器,给不同的实例配置不同的拦截器,支持以对象形式接受多个拦截器配置
4,一般情况下一个实例就够用了,在大行项目中可以使用到多个实例
目录结构:
类型声明:
/*------------------------- TS封装拦截器的接口 -------------------------------------*///导入axios的 实例的类型 请求参数的类型 响应数据的类型
import type { AxiosRequestConfig } from 'axios'//扩展拦截器的类型,支持传入一个对象,对象中可同时传入多个回调,可以同时包含响应器和拦截器的配置
export interface MyInterceptors {//请求拦截器和请求拦截器捕获错误的类型resquestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfigresquestInterceptorsCatch?: (err: any) => any//响应拦截器和响应拦截器捕获错误的类型responseInterceptors?: (config: any) => any //AxiosResponseresponseInterceptorsCatch?: (err: any) => any
}
//类型扩展,把axios上的类型,扩展到自己定义的接口上,使请求参数支持传入一个对象
export interface MyRequestConfig extends AxiosRequestConfig {interceptors?: MyInterceptors //扩展后的拦截器showLoading?: boolean //是否显示加载动画
}
基于axios封装的类:
//封装一个axios的类,每次使用时生成一个新的实例,这样配置多种不同服务器请求的接口
import axios from 'axios'
//导入axios的 实例的类型
import type { AxiosInstance } from 'axios'
//导入扩展后的接口类型 和扩展后的请求参数类型
import type { MyInterceptors, MyRequestConfig } from './types'
//导入请求动画组件
import { ElLoading } from 'element-plus'
// 导入请求动画实例的类型,用于取消动画
import { LoadingInstance } from 'element-plus/lib/components/loading/src/loading'
//封装axios
class MyAxios {instance: AxiosInstance //实例interceptors?: MyInterceptors //拦截器loading?: LoadingInstance //加载动画showLoading?: boolean //是否显示请求的动画//config的类型改为加了自己扩展后的类型:AxiosRequestConfig--->MyRequestConfig,//上面会增加interceptors接口,支持同时传入多个拦截器,和接受是否显示动画的配置constructor(config: MyRequestConfig) {//每次调用instance都会产生一个新的实例,在新的实例上面配置新的baseurl等配置this.instance = axios.create(config)//保存一份传入的所有拦截器this.interceptors = config.interceptors//默认显示请求动画:showLoading默认为truethis.showLoading = config.showLoading ?? true//使用实例身上的请求拦截器和响应拦截器的方法,把传入拦截器对象中的方法依次传入this.instance.interceptors.request.use(this.interceptors?.resquestInterceptors,this.interceptors?.resquestInterceptorsCatch)this.instance.interceptors.response.use(this.interceptors?.responseInterceptors,this.interceptors?.responseInterceptorsCatch)//但是有些拦截器配置每个实例都会需要,//如请求时的动画,需要全局配置,让每个实例中都存在,就直接使用axios上面的拦截器进行封装this.instance.interceptors.request.use((config) => {console.log('全局拦截成功')//全局配置loading动画,拿下loadingif (this.showLoading)this.loading = ElLoading.service({lock: true,text: '正在加载...',background: 'rgba(0,0,0,0.5)',})return config},(err) => {console.log('全局拦截失败')return err})this.instance.interceptors.response.use((res) => {setTimeout(() => {this.loading?.close()}, 1000)console.log('全局响应成功')const data = res.dataif (data.returnCode == '-1001') {console.log('请求失败')} else {return data}},(err) => {console.log('全局响应失败')this.loading?.close() //移除加载动画if (err.response.status === 404) {console.log('404错误信息')}return err})}//封装request请求//请求参数config要用自己扩展后的接口(MyRequestConfig),才支持传入对象形式的拦截器request<T>(config: MyRequestConfig): Promise<T> {return new Promise((resolve, resject) => {//还可以给单个请求配置拦截器if (config.interceptors?.resquestInterceptors) {//如果存在说明配置了单个请求的拦截器,就把转换后的config返回给config继续处理//config.interceptors.resquestInterceptors(config)返回的是一个处理过后的configconfig = config.interceptors.resquestInterceptors(config)}//请求动画默认false,如果showloading为false,就是关闭请求动画if (config.showLoading === false) {this.showLoading = config.showLoading}//使用axios自身的request发请求this.instance.request<any, T>(config).then((res) => {//如果为真说明配置了单个请求的响应拦截器,就把转换后的res返回给res继续处理//config.interceptors.resquestInterceptors(res)返回的是一个处理过后的resif (config.interceptors?.responseInterceptors) {res = config.interceptors.responseInterceptors(res)}//让动画显示,即使上面不显示动画的请求完也设置,这样不影响后面的动画显示this.showLoading = trueresolve(res)}).catch((err) => {this.showLoading = trueresject(err)})})}//封装get请求,就是再get内部调requestget<T>(config: MyRequestConfig): Promise<T> {return this.request<T>({ ...config, method: 'GET' })}//封装post请求,就是再post内部调requestpost<T>(config: MyRequestConfig): Promise<T> {return this.request<T>({ ...config, method: 'GET' })}//封装delete请求,就是再delete内部调requestdelete<T>(config: MyRequestConfig): Promise<T> {return this.request<T>({ ...config, method: 'GET' })}//封装patch请求,就是再patch内部调requestpatch<T>(config: MyRequestConfig): Promise<T> {return this.request<T>({ ...config, method: 'GET' })}
}
export default MyAxios
创建实例:
//配置MyAxios的实例并导出
import MyAxios from './request/index' //导入axios
import { BASE_URL, TIME_OUT } from '@/service/request/config' //导入环境变量
const myAxios = new MyAxios({baseURL: BASE_URL,timeout: TIME_OUT,//可选,单个实例的拦截器interceptors: {resquestInterceptors(config) {console.log('实例请求拦截成功')return config},resquestInterceptorsCatch(err) {console.log('实例请求拦截失败')return err},responseInterceptors(res) {console.log('实例响应拦截成功')return res},responseInterceptorsCatch(err) {console.log('实例请求拦截失败')return err},},
})
//导出封装的实例
export default myAxios
发送请求演示:
得到的返回值是我们传入接口的类型