monorepo中AXIOS拦截器的应用
Axios作为promise封装的http库在我们日常开发中极为常见了,对于axios的二次封装这边就不过多赘述了,这里主要谈一下monorepo对于axios拦截器的应用。monorepo的特性就是通用与个性,不同的H5页面互相独立,但是往往又能互相复用许多东西,比如登录态、loading状态等等。这些就可以用Axios拦截器来做统一管理,但是每个H5项目总会遇到对自己个性的接口进行拦截处理,所以我们拦截器的模型设计也可以参照上一章的vuex的设计。
目录结构的调整
├── config // vite相关配置
├── src
| ├── common // 通用方法
| | ├── store
| | ├── service // 新增axios目录
| | | ├── index.ts // 通用拦截
| | | ├── type.ts
| ├── packages // 包文件
| | ├── demo1
| | | ├── api
| | | | ├── index.ts // 接口
| | | | ├── service.ts // 独有拦截
| | | ├── index.html
| | | ├── main.ts
| | | ├── config.json
| | | ├── App.vue
| | | ├── store
| | ├── demo2
| | | ├── api
| | | | ├── index.ts // 接口
| | | | ├── service.ts // 独有拦截
| | | ├── index.html
| | | ├── main.ts
| | | ├── App.vue
| | | ├── store
├── package.json
├── vite.config.ts
新增common目录下的service和每个demo目录下的service,分别用于管理通用拦截和实例独有拦截。
代码实现
通用service代码
// src/common/service/type.ts
import { AxiosRequestConfig, AxiosResponse } from 'axios'
// 声明实例独有拦截器
export interface IServiceInterceptors {
requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig | any
requestInterceptorCatch?: (error: any) => any
responseInterceptor?: (config: AxiosResponse) => AxiosResponse | any
responseInterceptorCatch?: (error: any) => any
}
export interface IServiceConfig extends AxiosRequestConfig {
interceptors?: IServiceInterceptors
options?: IOptions
}
interface IOptions {
showLoading?: boolean
}
// src/common/service/index.ts
import axios, { AxiosInstance } from 'axios'
import { IServiceConfig, IServiceInterceptors } from './type'
import $store from '@store'
let needLoadingRequestCount = 0
// 结合vuex控制loading
const showLoading = config => {
if (config.options) {
const { showLoading = false } = config.options
if (showLoading) {
needLoadingRequestCount++
}
}
if (needLoadingRequestCount === 1) {
console.log('开启loading')
$store.commit('commonModule/showLoading', isLoadingPartial)
}
}
const hideLoading = config => {
if (config.options) {
const { showLoading = false } = config.options
if (showLoading) {
needLoadingRequestCount--
}
}
if (needLoadingRequestCount <= 0) {
console.log('关闭loading')
$store.commit('commonModule/hideLoading')
}
}
class CommonService {
instance: AxiosInstance
interceptors?: IServiceInterceptors
constructor(config: IServiceConfig) {
this.instance = axios.create(config)
this.interceptors = config.interceptors
// 实例独有的请求拦截
this.instance.interceptors.request.use(
this.interceptors?.requestInterceptor,
this.interceptors?.requestInterceptorCatch
)
// 实例独有的响应拦截
this.instance.interceptors.response.use(
this.interceptors?.responseInterceptor,
this.interceptors?.responseInterceptorCatch
)
// 通用拦截器 实现loading开启关闭 取消重复请求
this.instance.interceptors.request.use(
config => {
showLoading(config)
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
this.instance.interceptors.response.use(
(res: any) => {
hideLoading(res.config)
return res.data
},
error => {
hideLoading(error.config || error)
return Promise.reject(error)
}
)
}
// 简单封装一下get方法
get<T = any>(config: IServiceConfig): Promise<T> {
return this.instance.request({
...config,
params: config.params || config.data,
data: undefined,
method: 'GET'
})
}
request<T = any>(config: IServiceConfig): Promise<T> {
return this.instance.request(config)
}
}
export { CommonService }
通用的service暴露了一个通用的service类,里面实现了通用的拦截器,再由各个demo去实现该类,即可实现共用通用拦截,独享自己拦截的功能。
// src/packages/demo1/api/service.ts
import { CommonService } from '@service'
const service = new CommonService({
interceptors: {
requestInterceptor: config => {
console.log('实例请求拦截成功')
return config
},
requestInterceptorCatch: err => {
console.log('实例请求拦截失败')
return Promise.reject(err)
},
responseInterceptor: res => {
console.log('实例响应拦截成功')
return res
},
responseInterceptorCatch: err => {
console.log('实例响应拦截失败')
return Promise.reject(err)
}
}
})
export default service
// src/packages/demo1/api/index.ts
import service from './service'
enum URL {
test = 'xxx'
}
interface GetTestData {
id: string
}
const getTest = async (data: GetTestData) => {
return await service.get({
url: URL.test,
data,
options: { showLoading: true }
})
}
export { getTest }
在项目中使用
import { getTest } from '../api'
onMounted(async () => {
const res = await getTest({ id: '123' })
console.log('res:', res)
})
运行结果:
待改进点:独立拦截目前每种类型只给了一个对象,在实例实现的时候每种拦截器只能传入一个方法,不太利于拓展,可以通过一个拦截器数组来封装多个拦截器,这个项目已经实现了,这里就不过多赘述了。当然axios的封装也可以打磨一下,这里为了演示功能只做了一个简单的封装。