使用插件化思想管理axios拦截器

1,053 阅读4分钟

前言

在日常开发中,封装请求拦截器是一个必不可少的环节。今天给大家分享一个插件化的请求拦截器的实现方式,通过插件化的方式,可以让我们的请求拦截器更加灵活,更加易于维护。

何为插件化

插件化是一种将应用程序的功能模块化的技术,通过插件化技术,我们可以将应用程序的功能模块化,使得应用程序的功能更加灵活,更加易于维护。

也就是说,我们将拦截器的功能模块化,通过插件的方式,将拦截器的功能模块化,使得拦截器的功能更加灵活,更加易于维护。

为什么要插件化

主流的中后台模板中,都会针对请求拦截器进行封装,但是个人感觉这些封装方式,并不会很友好:

  1. 代码耦合度高,不易维护
  2. 功能模块化程度低,不易扩展
  3. 代码冗余度高,不易复用
  4. 代码逻辑复杂,不易理解
  5. 可读性不友好

所以,我们需要一种更加灵活,更加易于维护的请求拦截器的实现方式,这就是插件化的请求拦截器。

进入正题

这是我在根据 Ray Template 对于公司内部的 SaaS 平台重构过程中,实现的一些拦截器插件。

image.png

可以看到,我们将请求拦截器的功能模块化,通过插件的方式,将拦截器的功能模块化,使得拦截器的功能更加灵活,更加易于维护。有针对性需求的时候,只需要按照约定好的方式,新增一个插件模块即可。

实现拦截器

现在需要以下的步骤进行操作:

  1. 实现 useAxiosInterceptor 方法
  2. 改造拦截器内部
  3. 导出使用

实现 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,有兴趣的小伙伴可以点击查看。

希望这个封装思路能够帮助到大家,也可以动动小手指,给作者点个小星星~