这篇文章封装的axios已经满足如下功能:
- 无处不在的代码提示;
- 灵活的拦截器;
- 可以创建多个实例,灵活根据项目进行调整;
- 每个实例,或者说每个接口都可以灵活配置请求头、超时时间等;
一 基础封装
//目录:src/service/request/index.ts
import axios from "axios";
import type{ AxiosInstance, AxiosRequestConfig } from "axios"
class HYRequest {
// 声明属性,并且我们希望instance类型与create出的类型一致(create里提供的,不用记)
instance: AxiosInstance
// 1.request实例 => axios实例
// 创建出来的每个实例都对应一个axios实例
constructor(config: AxiosRequestConfig) {
// config: any
// any类型不好,传入参数的类型不规范,所以改成AxiosRequestConfig类型(create里提供的,不用记)
this.instance = axios.create(
// 我们不希望baseURL,timeout写死,希望是别人传过来的,所以在创建实例的时候可以传过来config,在src/index.ts中创建实例
// {
// baseURL: "xxx",
// timeout: 10000
// }
config
)
}
// 2.封装网络请求的方法(主要封装request方法)
request(config: AxiosRequestConfig) {
return this.instance.request(config)
}
}
export default HYRequest
二 拦截器封装
类拦截器
类拦截器只需要在类中对
axios.create()
创建的实例调用interceptors
下的两个拦截器即可,代码如下:
//目录:src/request/index.ts
// 给每一个instance实例都添加拦截器
import axios from "axios"
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"
import type { HYRequestConfig } from "./type"
class HYRequest {
instance: AxiosInstance
// request实例 => axios的实例
constructor(config: HYRequestConfig) {
this.instance = axios.create(config)
// 使用类拦截器
// 每个instance实例都添加拦截器
this.instance.interceptors.request.use(config => {
// loading/token
console.log("全局请求成功的拦截")
return config
}, err => {
console.log("全局请求失败的拦截")
return err
})
this.instance.interceptors.response.use(res => {
console.log("全局响应成功的拦截")
return res.data
}, err => {
console.log("全局响应失败的拦截")
return err
})
}
export default HYRequest
实例拦截器
实例拦截器是为了保证封装的灵活性,因为每一个实例中的拦截后处理的操作可能是不一样的,所以在定义实例时,允许我们传入拦截器。
- 针对AxiosRequestConfig配置进行扩展,因为AxiosRequestConfig中不允许我们传入拦截器(interceptors),而hyRequest中又需要传入一interceptors,所以自定义一个HYRequestConfig,让其继承于AxiosRequestConfig,且允许传入拦截器,代码如下:
//目录:src/request/type.ts
import type { AxiosRequestConfig, AxiosResponse } from "axios"
interface HYInterceptors<T = AxiosResponse> {
//请求拦截
requestSuccessFn?: (config: AxiosRequestConfig) => AxiosRequestConfig
requestFailureFn?: (err: any) => any
//响应拦截
responseSuccessFn?: (res: T) => T
responseFailureFn?: (err: any) => any
}
//自定义传入的参数
export interface HYRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
interceptors?: HYInterceptors<T>
}
- 使用类拦截器:代码如下:
//目录:src/request/index.ts
import axios from "axios";
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"
import type { HYRequestConfig } from "./type"
// 针对AxiosRequestConfig配置进行扩展,因为AxiosRequestConfig中不允许我们传入拦截器(interceptors),而hyRequest中又需要传入一interceptors,所以自定义一个HYRequestConfig,让其继承于AxiosRequestConfig,且允许传入拦截器
// 在request/type.ts中封装了
class HYRequest {
// 声明属性,并且我们希望instance类型与create出的类型一致(create里提供的,不用记)
instance: AxiosInstance
// 1.request实例 => axios实例
// 创建出来的每个实例都对应一个axios实例
constructor(config: HYRequestConfig) {
// config: any
// any类型不好,传入参数的类型不规范,所以改成AxiosRequestConfig类型(create里提供的,不用记)
this.instance = axios.create(
// 我们不希望baseURL,timeout写死,希望是别人传过来的,所以在创建实例的时候可以传过来config,在src/index.ts中创建实例
// {
// baseURL: "xxx",
// timeout: 10000
// }
config)
//使用类拦截器
// 给每一个instance实例都添加拦截器
this.instance.interceptors.request.use(config => {
console.log("全局请求成功的拦截")
return config
}, err => {
console.log("全局请求失败的拦截")
return err
})
this.instance.interceptors.response.use(res => {
console.log("全局响应成功拦截")
return res.data
}, err => {
console.log("全局响应失败拦截")
return err
})
//使用实例拦截器
// 针对特定的hyRequest实例,比如hyRequest2,添加拦截器,
//hyRequest2详见创建实例的目录:src/service/index.ts
this.instance.interceptors.request.use(
config.interceptors?.requestSuccessFn,
config.interceptors?.requestFailureFn
)
this.instance.interceptors.response.use(
config.interceptors?.responseSuccessFn,
config.interceptors?.responseFailureFn
)
}
// 2.封装网络请求的方法(主要封装request方法)
request(config: AxiosRequestConfig) {
return this.instance.request(config)
}
}
export default HYRequest
接口拦截器
对单一接口进行拦截,例如同一个实例的不同接口有不同的拦截操作,比如hyRequest2的/entire/list不进行拦截,而对hyRequest2的/home/highscore进行拦截
//目录:src/request/index.ts
import axios from "axios";
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"
import type { HYRequestConfig } from "./type"
class HYRequest {
instance: AxiosInstance
constructor(config: HYRequestConfig) {
this.instance = axios.create(config)
//使用类拦截器
// 给每一个instance实例都添加拦截器
this.instance.interceptors.request.use(config => {
console.log("全局请求成功的拦截")
return config
}, err => {
console.log("全局请求失败的拦截")
return err
})
this.instance.interceptors.response.use(res => {
console.log("全局响应成功拦截")
return res.data
}, err => {
console.log("全局响应失败拦截")
return err
})
//使用实例拦截器
// 针对特定的hyRequest实例,比如hyRequest2,添加拦截器,
//hyRequest2详见创建实例的目录:src/service/index.ts
this.instance.interceptors.request.use(
config.interceptors?.requestSuccessFn,
config.interceptors?.requestFailureFn
)
this.instance.interceptors.response.use(
config.interceptors?.responseSuccessFn,
config.interceptors?.responseFailureFn
)
}
// 使用接口拦截器
request<T = any>(config: HYRequestConfig<T>) {
// 单次请求的成功拦截处理
if (config.interceptors?.requestSuccessFn) {
config = config.interceptors.requestSuccessFn(config)
}
// return this.instance.request(config)
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)
})
})
}
}
export default HYRequest
三 创建实例
这里创建两个实例:hyRequest和hyRequest2
//目录:src/service/index.ts
import { BASE_URL, TIME_OUT } from "./config/index";
import HYRequest from "./request/index";
// 这里可以根据不同的url创建不同的实例:
// hyRequest,hyRequest1,hyRequest2···
const hyRequest = new HYRequest({
// config/index.ts中导出的BASE_URL,TIME_OUT
baseURL: BASE_URL,
timeout: TIME_OUT
})
const hyRequest2 = new HYRequest({
baseURL: "http://codercba.com:1888/airbnb/api",
timeout: 8000,
//使用实例拦截器
interceptors: {
requestSuccessFn: (config) => {
console.log("爱彼迎的请求成功的拦截")
return config
},
requestFailureFn: (err) => {
console.log("爱彼迎的请求失败的拦截")
return err
},
responseSuccessFn: (res) => {
console.log("爱彼迎的响应成功的拦截")
return res
},
responseFailureFn: (err) => {
console.log("爱彼迎的响应失败的拦截")
return err
}
}
})
export default hyRequest
export default hyRequest2
//目录:src/service/config/index.ts
export const BASE_URL = "http://codercba.com:8000"
export const TIME_OUT = 10000
四 发送网络请求
//目录:src/modules/home.ts
import hyRequest from "../index";
//根据数据拿到的类型
interface IHomeData {
data: any,
returnCode: string,
success: boolean
}
hyRequest.request<IHomeData>({
url: "/home/multidata"
}).then(res => {
console.log(res)
})
//目录:src/modules/entirs.ts
import { hyRequest2 } from "..";
// "/entire/list"不添加拦截器
hyRequest2.request({
url: "/entire/list",
params: {
offset: 0,
size: 20
}
}).then(res => {
console.log(res)
})
//根据数据拿到的类型
interface IHighScoreData {
list: any[],
subtitle: string,
title: string
type: string,
_id: string
}
// "/home/highscore"添加拦截器
hyRequest2.request<IHighScoreData>({
url: "/home/highscore",
interceptors: {
requestSuccessFn: (config) => {
console.log("/home/highscore请求成功的拦截")
return config
},
responseSuccessFn: (res) => {
console.log("/home/highscore响应成功的拦截")
return res
},
}
}).then(res => {
console.log(res)
})
五 运行代码
// src/index.ts
import "./service/modules/home"
import "./service/modules/entirs"
// 加入webpack依赖图,里面的代码自然可以运行
注意:TS在webpack下的运行环境在之前的文章中写过