在项目中用ts封装axios
基础封装
import axios from 'axios'
import type {AxiosInstance,AxiosRequestConfig,AxiosResponse} from 'axios'
class Request{
//axios实例,并将它作为 类的instance属性(属性名自定义)
instance:AxiosInstance
constructor(config:AxiosRequestConfig){
this.instance = axios.create(config)
}
request(config:AxiosRequestConfig){
return this.instanse.request(config) //类的方法request是绑定在原型上的,所以实例也可以访问到
}
}
这里讲题封装为一个类,而不是一个函数,是因为类可以创建多个实例,适用范围更广,封装性更强。
拦截器封装
先封装一下拦截器,这个拦截器分三种:
- 类拦截器
- 实例拦截器
- 接口拦截器
1. 类拦截器
类拦截器比较容易实现,只要在类中对axios.create()
创建的实例,调用interceptors
下的两个拦截器即可,实例代码如下:
constructor(config:AxiosRequestConfig){
this.instance = axios.create(config)
this.instance.interceptors.request.use(
(res:AxiosRequestConfig )=>{
console.log('全局请求拦截器')
return res
},
(err:any)=>err,
)
this.instance.interceptors.response.use(
(res.AxiosResponse)=>{
console.log('全局响应拦截器')
return res.data,//这里对响应拦截器做了一个简单的处理,就是将请求进行返回,因为我们的接口请求中的数据主要存放在.data中,跟.data同级的属性基本不需要
}
)
}
2. 实例拦截器
实例拦截器是为了保证封装的灵活性,因为每一个实例中拦截后的操作可能不一样,所以定义实例时,允许我们传入拦截器。
首先我们定义一下 interface,方便类型提示:
//types.ts
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
export interface RequestInterceptors {
// 请求拦截
requestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfig
requestInterceptorsCatch?: (err: any) => any
// 响应拦截
responseInterceptors?: (config: AxiosResponse) => AxiosResponse
responseInterceptorsCatch?: (err: any) => any
}
// 自定义传入的参数
export interface RequestConfig extends AxiosRequestConfig {
interceptors?: RequestInterceptors
}
定义好基础的拦截器后,我们需要改造我们传入的参数的类型,因为axios提供的AxiosRequestConfig
是不允许我们传入拦截器的,所以说我们自定义了RequestConfig
,让其继承与AxiosRequestConfig
。
剩余部分的代码:
//index.ts
import axios Axios,{AxiosResponse} from 'axios'
import type {AxiosInstance,AxiosRequestConfig} from 'axios'
import type{RequestConfig,RequestInterceptors} from './types.ts'
class Request{
//axios实例
instance:AxiosInstance
//拦截器对象
interceptorsObj?:RequestInterceptors
constructor(config:RequestConfig){
this.instance = axios.create(config)
this.interceptorsObj = config.interceptors
this.instance.interceptors.request.use(
(res: AxiosRequestConfig)=>{
console.log('全局请求拦截器')
return res
},
(err: any)=>{
console.log(err)
}
)
// 使用实例拦截器
this.instance.interceptors.request.use(
this.interceptorsObj?.requestInterceptors,
this.interceptorsObj?.requestInterceptorsCatch,
)
this.instance.interceptors.response.use(
this.interceptorsObj?.responseInterceptors,
this.interceptorsObj?.responseInterceptorsCatch,
)
// 全局响应拦截器保证最后执行
this.instance.interceptors.response.use(
// 因为我们接口的数据都在res.data下,所以我们直接返回res.data
(res: AxiosResponse) => {
console.log('全局响应拦截器')
return res.data
},
(err: any) => err,
)
拦截器的执行顺序为:实例请求–>类请求–>实例响应–>类响应,这样我们就可以在实例拦截器上做出不同的拦截。
3. 接口拦截
现在我们对单一接口进行拦截操作,首先我们将AxiosRequestConfig
类型修改为RequestConfig
允许传递拦截器;然后我们在类拦截器中将接口请求的数据进行了返回,也就是说在require()
方法中得到的类型就不是AxiosResponse
类型了。
我们查看axios的index.d.ts
中,对require()
方法的类型定义如下:
// type.ts
request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
复制代码
也就是说它允许我们传递类型,从而改变require()
方法的返回值类型,我们的代码如下:
// index.ts
request<T>(config: RequestConfig): Promise<T> {
return new Promise((resolve, reject) => {
// 如果我们为单个请求设置拦截器,这里使用单个请求的拦截器
if (config.interceptors?.requestInterceptors) {
config = config.interceptors.requestInterceptors(config)
}
this.instance
.request<any, T>(config)
.then(res => {
// 如果我们为单个响应设置拦截器,这里使用单个响应的拦截器
if (config.interceptors?.responseInterceptors) {
res = config.interceptors.responseInterceptors<T>(res)
}
resolve(res)
})
.catch((err: any) => {
reject(err)
})
})
}
这里还存在一个细节,就是我们在拦截器接受的类型一直是AxiosResponse
类型,而在类拦截器中已经将返回的类型改变,所以说我们需要为拦截器传递一个泛型,从而使用这种变化,修改types.ts
中的代码,示例如下:
// index.ts
export interface RequestInterceptors {
// 请求拦截
requestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfig
requestInterceptorsCatch?: (err: any) => any
// 响应拦截
responseInterceptors?: <T = AxiosResponse>(config: T) => T
responseInterceptorsCatch?: (err: any) => any
}
请求接口拦截是最前执行,而响应拦截是最后执行。
未完。。。