前言
在日常开发中,封装请求拦截器是一个必不可少的环节。今天给大家分享一个插件化的请求拦截器的实现方式,通过插件化的方式,可以让我们的请求拦截器更加灵活,更加易于维护。
何为插件化
插件化是一种将应用程序的功能模块化的技术,通过插件化技术,我们可以将应用程序的功能模块化,使得应用程序的功能更加灵活,更加易于维护。
也就是说,我们将拦截器的功能模块化,通过插件的方式,将拦截器的功能模块化,使得拦截器的功能更加灵活,更加易于维护。
为什么要插件化
主流的中后台模板中,都会针对请求拦截器进行封装,但是个人感觉这些封装方式,并不会很友好:
- 代码耦合度高,不易维护
- 功能模块化程度低,不易扩展
- 代码冗余度高,不易复用
- 代码逻辑复杂,不易理解
- 可读性不友好
所以,我们需要一种更加灵活,更加易于维护的请求拦截器的实现方式,这就是插件化的请求拦截器。
进入正题
这是我在根据 Ray Template 对于公司内部的 SaaS 平台重构过程中,实现的一些拦截器插件。
可以看到,我们将请求拦截器的功能模块化,通过插件的方式,将拦截器的功能模块化,使得拦截器的功能更加灵活,更加易于维护。有针对性需求的时候,只需要按照约定好的方式,新增一个插件模块即可。
实现拦截器
现在需要以下的步骤进行操作:
- 实现
useAxiosInterceptor方法 - 改造拦截器内部
- 导出使用
实现 useAxiosInterceptor 方法
export const useAxiosInterceptor = () => {
/** 创建拦截器实例 */
const createAxiosInstance = (
instance: RequestInterceptorConfig | ResponseInterceptorConfig,
instanceKey: keyof AxiosFetchInstance,
) => {
instanceKey === 'requestInstance'
? (axiosFetchInstance['requestInstance'] =
instance as RequestInterceptorConfig)
: (axiosFetchInstance['responseInstance'] =
instance as ResponseInterceptorConfig)
}
/** 获取当前实例 */
const getAxiosInstance = (instanceKey: keyof AxiosFetchInstance) => {
return axiosFetchInstance[instanceKey]
}
/** 设置注入方法队列 */
const setImplement = (
key: keyof ImplementQueue | keyof ErrorImplementQueue,
func: AnyFC[],
fetchType: FetchType,
) => {
fetchType === 'ok' ? (implement[key] = func) : (errorImplement[key] = func)
}
/** 获取队列中所有的所有拦截器方法 */
const getImplement = (
key: keyof ImplementQueue | keyof ErrorImplementQueue,
fetchType: FetchType,
): AnyFC[] => {
return fetchType === 'ok' ? implement[key] : errorImplement[key]
}
/** 队列执行器 */
const implementer = (funcs: AnyFC[], ...args: any[]) => {
if (Array.isArray(funcs)) {
funcs.forEach((curr) => {
if (typeof curr === 'function') {
curr(...args)
}
})
}
}
/** 请求、响应前执行拦截器队列中的所有方法 */
const beforeFetch = (
key: keyof AxiosFetchInstance,
implementKey: keyof ImplementQueue | keyof ErrorImplementQueue,
fetchType: FetchType,
) => {
const funcArr =
fetchType === 'ok'
? implement[implementKey]
: errorImplement[implementKey]
const instance = getAxiosInstance(key)
const { MODE } = getAppEnvironment()
if (instance) {
implementer(funcArr, instance, MODE)
}
}
/** 请求、响应错误时执行队列中所有方法 */
const fetchError = (
key: keyof AxiosFetchError,
error: AxiosError<unknown, unknown>,
errorImplementKey: keyof ErrorImplementQueue,
) => {
axiosFetchError[key] = error
const funcArr = errorImplement[errorImplementKey]
const { MODE } = getAppEnvironment()
implementer(funcArr, error, MODE)
}
return {
createAxiosInstance,
setImplement,
getImplement,
getAxiosInstance,
beforeFetch,
fetchError,
}
}
改造拦截器
import axios from 'axios'
import { AXIOS_CONFIG } from '@/app-config'
import { useAxiosInterceptor } from '@/axios/utils/interceptor'
import {
setupResponseInterceptor,
setupResponseErrorInterceptor,
} from '@/axios/axios-interceptor/response'
import {
setupRequestInterceptor,
setupRequestErrorInterceptor,
} from '@/axios/axios-interceptor/request'
import type { AxiosInstanceExpand } from './types'
const server: AxiosInstanceExpand = axios.create(AXIOS_CONFIG)
const { createAxiosInstance, beforeFetch, fetchError } = useAxiosInterceptor()
// 请求拦截器
server.interceptors.request.use(
(request) => {
createAxiosInstance(request, 'requestInstance') // 生成 request instance
setupRequestInterceptor() // 初始化拦截器所有已注入方法
beforeFetch('requestInstance', 'implementRequestInterceptorArray', 'ok') // 执行拦截器所有已注入方法
return request
},
(error) => {
setupRequestErrorInterceptor() // 初始化拦截器所有已注入方法(错误状态)
fetchError('requestError', error, 'implementRequestInterceptorErrorArray') // 执行所有已注入方法
return Promise.reject(error)
},
)
// 响应拦截器
server.interceptors.response.use(
(response) => {
createAxiosInstance(response, 'responseInstance') // 创建响应实例
setupResponseInterceptor() // 注入响应成功待执行队列
beforeFetch('responseInstance', 'implementResponseInterceptorArray', 'ok') // 执行响应成功拦截器
const { data } = response
return Promise.resolve(data)
},
(error) => {
setupResponseErrorInterceptor() // 注入响应失败待执行队列
fetchError('responseError', error, 'implementResponseInterceptorErrorArray') // 执行响应失败后拦截器
return Promise.reject(error)
},
)
export default server
使用 axios instance
仅需要导入使用即可。
import { request } from '@/axios'
request({
url: 'xxx',
method: 'xxx',
})
实现插件
我们举个例子,实现一个根据响应值弹窗的插件。
injectResponseAlert
import type { AxiosResponseInterceptor } from '@/axios/types'
export const injectResponseAlert: AxiosResponseInterceptor = (ins, mode) => {
const {
data: { data },
} = ins
if (data) {
alert(JSON.stringify(data))
}
}
注册插件
在 implementResponseInterceptorArray 队列中注册即可。
结语
以上代码有很多省略,具体代码来自于 Ray Template AxiosInterceptor,有兴趣的小伙伴可以点击查看。
希望这个封装思路能够帮助到大家,也可以动动小手指,给作者点个小星星~