vue3+ts中台项目小结之axios请求封装

您所在的位置:网站首页 localhost8080拒绝了我们的连接请求怎么办 vue3+ts中台项目小结之axios请求封装

vue3+ts中台项目小结之axios请求封装

2023-03-30 05:21| 来源: 网络整理| 查看: 265

公司平台新搭建了一个中台系统吧,自己在忙别的项目了,几乎没有什么参与度,作为被边缘化的成员,自己还是要努把力,多多学习,争取下进步,加油derder~!

项目是vite搭建的,重点来看下axios文件夹中封装的内容(axios安装过程这里就略过了)。闲话不多说,首先来看下目录结构的:

axios ├─ config │ └─ index.ts ├─ plugins │ ├─ axios-controller.plugin.ts │ ├─ axios-debounce.plugin.ts │ ├─ axios-exception.plugin.ts │ ├─ axios-extend.plugin.ts │ ├─ axios-path-to-regexp.ts │ ├─ axios-retry.plugin.ts │ └─ axios-token.plugin.ts ├─ index.ts └─ interface.ts 复制代码

就从axios文件夹中根目录的index.ts文件:

import { axiosExceptionPlugin } from './plugins/axios-exception.plugin' import axios from 'axios' import config from './config' import { getTokenLocalApi } from '@/service/local-api/token/token.local' import axiosProCreator from './plugins/axios-extend.plugin' import AxiosDebouncePlugin from './plugins/axios-debounce.plugin' import { axiosTokenPlugin } from './plugins/axios-token.plugin' import { axiosRetryPlugin } from './plugins/axios-retry.plugin' import { axiosPathToRegexp } from './plugins/axios-path-to-regexp' import { axiosControllerPlugin } from './plugins/axios-controller.plugin' const fetch = axios.create(config.defaultConfig) const axiosPro = axiosProCreator(fetch) /** * 异常捕获插件 */ axiosPro.use(axiosExceptionPlugin, config.pluginConfig.exceptionConfig) /** * 重试插件 */ axiosPro.use(axiosRetryPlugin, config.pluginConfig.retryPluginConfig) /** * 令牌插件 */ axiosPro.use(axiosTokenPlugin, { tokenResource: async () => (await getTokenLocalApi())?.token, tokenName: config.pluginConfig.tokenPluginConfig.name }) /** * path替换插件 */ axiosPro.use(axiosPathToRegexp) /** * 请求防抖插件 */ axiosPro.use(new AxiosDebouncePlugin()) /** * 控制器插件 */ axiosPro.use(axiosControllerPlugin) export default axiosPro 复制代码

分析:

首先来看axios.create(自定义的配置,例如baseUrl等)来创建一个新的axios命名为fetch(配置文件后面会列出)

接着来看下import config from './config'中的配置内容:

/** * @ Description: axios配置 */ import { AXIOS_REQUEST_TIMEOUT, AXIOS_RETRIES, AXIOS_TOKEN_NAME, AXIOS_DELAY_TIME, AXIOS_BASE_URL, exceptionCode, exceptionWhitelist } from '@/config/axios.config' export default { /** * 基础默认配置 */ defaultConfig: { baseURL: AXIOS_BASE_URL, timeout: AXIOS_REQUEST_TIMEOUT }, /** * 插件配置 */ pluginConfig: { /** * 重试插件 */ retryPluginConfig: { retries: AXIOS_RETRIES, // 重试次数 delayTime: AXIOS_DELAY_TIME // 每次重试之间的实践间隔 }, /** * 令牌插件 */ tokenPluginConfig: { name: AXIOS_TOKEN_NAME }, /** * 异常捕获配置 */ exceptionConfig: { /** * 异常code */ exceptionCode: exceptionCode, /** * 白名单 */ whitelist: exceptionWhitelist } } } 复制代码

配置文件中,还有配置文件

import { AXIOS_REQUEST_TIMEOUT, AXIOS_RETRIES, AXIOS_TOKEN_NAME, AXIOS_DELAY_TIME, AXIOS_BASE_URL, exceptionCode, exceptionWhitelist } from '@/config/axios.config'

import type { AxiosResponse } from 'axios' /** * 请求超时时长(单位:毫秒) */ export const AXIOS_BASE_URL = '/api' /** * 请求超时时长(单位:毫秒) */ export const AXIOS_REQUEST_TIMEOUT = 20000 /** * 令牌名称 */ export const AXIOS_TOKEN_NAME = 'Authorization' /** * 令牌名称 */ export const AXIOS_REFRESH_TOKEN_NAME = 'refresh_token' /** * 令牌刷新时间差 单位:分组 */ export const AXIOS_REFRESH_DIFFERENCE_MINUTES = 1 /** * 令牌头前缀 */ export const AXIOS_TOKEN_CONTENT_PREFIX = 'Bearer ' /** * 失败重试次数 */ export const AXIOS_RETRIES = 3 /** * 失败重试时间间隔 */ export const AXIOS_DELAY_TIME = 1500 /** * 异常code */ export const exceptionCode = [] /** * 异常白名单 */ export const exceptionWhitelist = (response: AxiosResponse) => response.data?.status 复制代码

里面设置了一些常用的涉及请求的常量,这里不一一介绍了的。

在index.ts文件中可以看到const axiosPro = axiosProCreator(fetch),那么我们就来看下的这个axiosProCreator是啥

//axiosProCreator /** * @ Description: axios扩展插件 */ import type { AxiosInstance, AxiosStatic } from 'axios' import type { AxiosPlugin } from '../interface' declare module 'axios' { interface Axios { use(axiosPlugin: AxiosPlugin, options?: any): void } } export default function axiosProCreator(axios: AxiosStatic | AxiosInstance) { axios.use = function (axiosPlugin: AxiosPlugin, options?: any) { axiosPlugin.install(axios, options) } return axios } 复制代码

分析:

ts中的 declare module 意为声明模块。www.bilibili.com/video/BV179… axiosProCreator函数中,为传递进来的axios参数添加了use方法,内部调用了install方法,然后返回参数axios

interface.ts接口类型文件:

import type { AxiosInstance, AxiosStatic } from 'axios' export interface AxiosPlugin { install: (axios: AxiosStatic | AxiosInstance, options: any) => void } export interface AxiosPluginInstance { install(axios: AxiosStatic | AxiosInstance, options: any): void } 复制代码

分析:

定义了接口类型,接口AxiosPlugin和AxiosPluginInstance都要有一个install方法

接下来看下第一个插件的axiosExceptionPlugin--异常插件:

/** * @ Description: 异常插件 */ import type { AxiosPlugin } from '../interface' import type { AxiosInstance, AxiosResponse, AxiosStatic } from 'axios' import isArray from 'lodash/fp/isArray' import isFunction from 'lodash/fp/isFunction' interface Options { /** * 异常code */ exceptionCode: string[] /** * 白名单 */ whitelist: string[] | ((response: AxiosResponse) => Boolean) } export const axiosExceptionPlugin: AxiosPlugin = { install: (axios: AxiosStatic | AxiosInstance, options: Options) => { axios.interceptors.response.use( (response) => { // 2xx 范围内的状态码都会触发该函数。 const code = response.data?.code const whitelist = options.whitelist if ( /** * 数组,code在白名单中 */ (isArray(whitelist) && whitelist.includes(code)) || /** * 函数返回值为真 */ (isFunction(whitelist) && whitelist(response)) ) { return response } return Promise.reject(response.data) }, function (error) { // 超出 2xx 范围的状态码都会触发该函数。 // 对响应错误做点什么 return Promise.reject(error) } ) } } 复制代码

分析:

axiosExceptionPlugin插件中有一个install方法,通过axios.interceptors.response做了一层响应拦截,响应码状态码在2xx范围内,或者在白名单中的话,就返回数据response。

然后是重试插件-axiosRetryPlugin:

// /** * @ name: axiosRetryPlugin * @ Description: 错误重试插件 */ import { AxiosError, type AxiosInstance, type AxiosStatic } from 'axios' import type { AxiosPlugin } from '../interface' interface Options { retries: number // 重试次数 delayTime: number // 每次重试之间的实践间隔为 retry count * delayTime } declare module 'axios' { interface AxiosRequestConfig { retryIndex?: number } } /** * axios重试插件 * @param axios * @returns */ export const axiosRetryPlugin: AxiosPlugin = { install: (axios: AxiosStatic | AxiosInstance, options: Options) => { axios.interceptors.response.use(undefined, function (error) { if (error instanceof AxiosError) { const { config } = error if (config?.retryIndex === undefined) config.retryIndex = 1 if (config.retryIndex { setTimeout(() => { if (config.retryIndex) config.retryIndex++ return resolve(axios(config)) }, options.delayTime * (config.retryIndex || 1)) }) } return Promise.reject(error) } return Promise.reject(error) }) return axios } } 复制代码

接着继续看下令牌插件-axiosTokenPlugin:

/** * @ name:axiosTokenPlugin * @ Description: 令牌插件 */ import { AXIOS_TOKEN_CONTENT_PREFIX } from '@/config/axios.config' import type { AxiosInstance, AxiosStatic } from 'axios' import type { AxiosPlugin } from '../interface' /** * axios token挂载插件 * @param axios * @returns */ interface Options { tokenName: string // 令牌名称 tokenResource: () => any // token资源 } export const axiosTokenPlugin: AxiosPlugin = { install: (axios: AxiosStatic | AxiosInstance, options: Options) => { axios.interceptors.request.use(async (config) => { const token = await options.tokenResource() if (config && config.headers && token) config.headers[options.tokenName] = `${AXIOS_TOKEN_CONTENT_PREFIX}${token}` return config }) } } 复制代码

path替换插件-axiosPathToRegexp:

/** * @ Name: axiosPathToRegexp * @ Description: 路径替换插件 */ import type { AxiosPlugin } from '../interface' import type { AxiosInstance, AxiosStatic } from 'axios' import { compile } from 'path-to-regexp' declare module 'axios' { interface AxiosRequestConfig { pathParams?: Record } } export const axiosPathToRegexp: AxiosPlugin = { install: (axios: AxiosStatic | AxiosInstance) => { axios.interceptors.request.use((config) => { if (config.pathParams && config.url) { config.url = compile(config.url)(config.pathParams) } return config }) } } 复制代码

请求防抖插件-AxiosDebouncePlugin :

/** * @ Name: AxiosDebouncePlugin * @ Description: 防抖插件 */ import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosStatic } from 'axios' import type { AxiosPluginInstance } from '../interface' declare module 'axios' { interface Axios { $get( url: string, config?: AxiosRequestConfig ): Promise $post( url: string, data?: D, config?: AxiosRequestConfig ): Promise $delete( url: string, config?: AxiosRequestConfig ): Promise $put( url: string, data?: D, config?: AxiosRequestConfig ): Promise $patch( url: string, data?: D, config?: AxiosRequestConfig ): Promise } } type Method = 'get' | 'post' | 'delete' | 'put' | 'patch' export interface Options { methods: Method[] } const DEFAULT_OPTIONS: Options = { methods: ['get', 'post', 'delete', 'put', 'patch'] } class AxiosDebouncePlugin implements AxiosPluginInstance { /** * 执行区 */ private execution: Record = {} /** * 默认配置 */ private defaultOptions: Options constructor(private options?: Options) { this.defaultOptions = Object.assign({}, DEFAULT_OPTIONS, options) } /** * 执行区增加新成员 * @param key * @param calFun */ private pushExecution(key: string, calFun: Promise) { this.execution[key] = calFun } /** * 移除执行区 * @param key */ private removeExecution(key: string) { delete this.execution[key] } /** * 是否正在执行 * @param key * @returns */ isPending(key: string) { return Reflect.has(this.execution, key) } /** * key值生成器 * @param url * @param data * @returns */ keyCreator(url: string, data: Record) { return `${url}: ${JSON.stringify(data)}` } /** * 执行拦截器 * @param target * @param argArray * @returns */ exeInterceptor( target: | (( url: string, config?: AxiosRequestConfig | undefined ) => Promise) | (( url: string, data?: D, config?: AxiosRequestConfig ) => Promise), argArray: any[] ) { const key = this.keyCreator(argArray[0], argArray[1]) if (this.isPending(key)) { /** * 正在执行中 */ return this.execution[key] } else { /** * 执行区中无此项实例正在执行 */ const call = target(argArray[0], argArray[1], argArray[2]).finally(() => this.removeExecution(key) ) this.pushExecution(key, call) return call } } /** * 插件安装 * @param axios */ install(axios: AxiosStatic | AxiosInstance) { this.defaultOptions.methods.forEach((method) => { if (method === 'get' || method === 'delete') { axios[`$${method}`] = new Proxy(axios[method], { apply: (target, thisArg, argArray) => { return this.exeInterceptor(target, argArray) } }) } else { axios[`$${method}`] = new Proxy(axios[method], { apply: (target, thisArg, argArray) => { return this.exeInterceptor(target, argArray) } }) } }) } } export default AxiosDebouncePlugin 复制代码

控制器插件-axiosControllerPlugin

/** * @ Name: axiosControllerPlugin * @ Description: 控制器插件 */ import type { AxiosInstance, AxiosStatic } from 'axios' import type { AxiosPlugin } from '../interface' declare module 'axios' { interface AxiosRequestConfig { controller?: AbortController } } /** * axios token挂载插件 * @param axios * @returns */ export const axiosControllerPlugin: AxiosPlugin = { install: (axios: AxiosStatic | AxiosInstance) => { axios.interceptors.request.use(async (config) => { config.signal = config.data?.$controller || config.params?.$controller delete config.data?.$controller delete config.params?.$controller return config }) } } 复制代码

主要就是弄明白弄清楚这种插拔式的拆分思路吧,理解和沉淀都需要时间,看完不是重点,弄会理解才是。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3