基于axios封装的方法工厂

文章介绍了如何通过封装axios来创建一个方法工厂,减少重复代码,实现拦截器的清晰管理,并支持动态配置请求参数。通过AxiosFactory和Request类,实现了请求的拦截器的定制和动态生成API请求方法,简化了项目中的API处理过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们在做项目的时候很多时候会在Api中定义很多请求的方法,导致我们会写很多的函数以及重复性的代码。为了解决这个问题,我基于axios封装了一个方法工厂。

使用方法工厂不需要在Api中再去写入很多的函数,只需要在对应的文件中配置好请求参数等信息,就可以根据所配置的请求信息动态的创建出所需要的方法随用随创建即可。

最后方法工厂所能达到的目的:

1.拦截器目的更具性处理
2.根据配置动态创建方法
3.可以支持默认参数
4.参数支持query拼接,body请求体,params地址动态参数

为了遵循设计模式单一职责需要创建两个类分别是AxiosFactoryRequest,其中AxiosFactory主要作用用来动态创建方法,Request用来处理axios相关内容。

class Request { //...
};
class AxiosFactory extends Request {//...
}; 

首先处理的是拦截器部分,在项目中拦截器在使用的时候不太方便使用,虽然在使用过程中已经区分了请求拦截和响应拦截,但是混在一起有的时候确实不太好区分,有的时候还有可能会搞混了,axios拦截器使用如下代码所示:

import axios from "axios";
import type {AxiosRequestConfig,AxiosResponse,AxiosError
} from "axios";

const request = axios.create({baseUrl: "https://juejin.cn/"
});
// 请求拦截
request.interceptors.request.use((config: AxiosRequestConfig): AxiosRequestConfig => {// 处理逻辑return config;
}, (error: AxiosError): AxiosError => {// 处理逻辑return error;
});
// 响应拦截
axiosInstance.interceptors.response.use((response: AxiosResponse): AxiosResponse => {// 处理逻辑return response;
}, (error: AxiosError): AxiosError => {// 处理逻辑return error;
}); 

这里对拦截器进行简易的封装,让拦截器看起来更具有针对性:

import axios from "axios";
import type {AxiosRequestConfig,AxiosResponse,AxiosError
} from "axios";
import type {Intercept
} from "types/Request";
// 接口应该放在 **.d.ts 中
interface Intercept { requestIntercept?: (config: AxiosRequestConfig) => AxiosRequestConfig;requestError?: (error: AxiosError) => AxiosError;responseIntercept?: (config: AxiosResponse) => AxiosResponse;responseError?: (error: AxiosError) => AxiosError;
}

class Request { private readonly axiosInstance;constructor(config: AxiosRequestConfig) { //axios实例this.axiosInstance = axios.create(config);};interceptors(intercept: Intercept): void { const { axiosInstance } = this;// 请求拦截函数const requestIntercept = intercept.requestIntercept || ((config: AxiosRequestConfig) => config);// 请求错误函数const requestError = intercept.requestError || ((error: AxiosError): AxiosError => error);// 响应拦截函数const responseIntercept = intercept.responseIntercept || ((config: AxiosResponse) => config);// 响应错误函数const responseError = intercept.responseError || ((error: AxiosError): AxiosError => error);// 请求拦截器axiosInstance.interceptors.request.use(requestIntercept, requestError);// 响应拦截器axiosInstance.interceptors.response.use(responseIntercept, responseError);};
} 

Request类中使用interceptors进行二次封装处理,使用对象对不同的拦截器的使用函数收,为了保证拦截器的正常工作,分别为其提供了不同的默认值。

在使用的时候只需要使用对象传入对应的方法即可,其实这里可以看出对弈拦截器的处理并没有做太多的工作,这样处理只是想让拦截器更加的清晰而已。

import Request from "utils/AxiosFactory/Request";
import type {AxiosRequestConfig,AxiosResponse,AxiosError
} from "axios";
// 创建axios实例
const request = new Request({baseURL: "https://juejin.cn/"
});
// 请求拦截函数
const requestIntercept = (config: AxiosRequestConfig): AxiosRequestConfig => {console.log("请求拦截");return config;
};
// 响应拦截函数
const responseIntercept = (response: AxiosResponse): AxiosResponse => {console.log("响应拦截");return response;
};
// 处理错误函数
const processingError = (error: AxiosError): AxiosError => {console.log("出错了");return error;
};
// 使用拦截器
request.interceptors({requestIntercept,requestError: processingError,responseIntercept,responseError: processingError
}); 

完成了对拦截器的处理,接下来就需要完成剩下的需求,支持可以配置化请求信息,这里的程序设想是这样的,需要把所需要的配置统一存储到类中,之后再通过方法动态创建出所需要的请求函数,还有一个比较重要的一点就是,一般项目中axios实例只会用到一个,很少情况会出现一个项目中会出现多个axios

定义了AxiosFactory工厂方法,为了保证axios实例有且只有一个,需要给AxiosFactory使用单例模式保证其只有一个实例,并在类中定义一个RequestConfigMap用来存储收集到的配置的请求信息。

import Request from "./Request";
import type { AxiosRequestConfig } from "axios";
import type {RequestItem
} from "types/Request";

class AxiosFactory extends Request {// 存储收集到的请求信息private RequestConfigMap:any = {};// 实例static instance:AxiosFactory;constructor(config: AxiosRequestConfig) { // 继承用于实例化axiossuper(config);// 实现单例if (AxiosFactory.instance === undefined) {AxiosFactory.instance = this;}return AxiosFactory.instance;};
}; 

AxiosFactory完成了基础工作,接下来需要做的就是收集依赖和创建对应的请求方法并返回。

// 接口应该放在 **.d.ts 中
interface RequestItem extends AxiosRequestConfig { key?: string; // 调用方法keyurl?: string; // 请求地址method?: Method; // 请求方式data?: any; // body 请求体query?: any; // query请求方式 ?拼接params?: any;// 路由动态参数 url/**/**onlyIn?: boolean; // 特有参数,是否只使用内部参数(后面会单独说明参数作用)
};

class AxiosFactory extends Request {// ... 其他代码// 依赖收集 public setConfig(name: string, requestConfigs: RequestItem[]): void { // 把收集到请求信息存储起来this.RequestConfigMap[name] = requestConfigs;};// 根据收集到的依赖动态创建方法public create(name: string): any {// 收集到的请求信息const { RequestConfigMap } = this;// 读取存储信息const requestConfigs = RequestConfigMap[name];// 存储创建方法let methodsMap: any = {};// 如果读取的请求信息不存在if (!RequestConfigMap) {// 提示错误console.error("未找到对应集合,请使用新建setConfig添加");return methodsMap;};// 遍历读取到的请求信息for (let i = 0; i < requestConfigs.length; i++) { // 请求信息每一项const requestItem: RequestItem = requestConfigs[i];// 读取key 用于最终调用方法const { key = "" } = requestItem;// 完成创建逻辑(后面会说明)const fn = this.createMethod(requestItem);// 添加所创建的方法methodsMap[key] = fn;}// 返回所有方法return methodsMap;};
} 

通过上述方法,完成了对请求信息的收集以及动态创建并且返回对应的方法,然而并没有实现如何动态创建方法,接下来需要完成这部分的代码,这部分代码是和axios相关,所以这部分代码写入到了Request类中。

class Request {// ... 其他代码// 创建请求方法// RequestItem 类型上面有介绍public createMethod(requestItem: RequestItem): Fun { // 获取axios实例const { axiosInstance } = this;// 请求信息const {// url地址url: beforeUrl = "",// 请求方式method = "get",// 默认请求体参数data: outData = {},// 默认query参数query: outQuery = {},// 默认params参数params: outParams = {},// 特有参数onlyIn: outOnlyIn = false} = requestItem;const that = this;// 返回方法return function (internalConfig: RequestItem = {}): any { const { // 内部请求体参数data: internalData = {},// 内部query参数query: internalQuery = {},// 内部params参数params: internalParams = {},// 特有参数onlyIn: internalOnlyIn = false} = internalConfig;// 是否内部const isIn = internalOnlyIn || outOnlyIn;// 请求信息const reuqestConfig: any = {};// 如果只使用内部if (!isIn) {// 合并请求体参数const afterData = that.margeData(outData, internalData);reuqestConfig.data = afterData;// 合并query参数const afterQuery = that.margeData(outQuery, internalQuery);reuqestConfig.params = afterQuery;} else { // 直接使用内部参数reuqestConfig.data = internalData;reuqestConfig.params = internalQuery;};// 如果是内部使用内部params参数const beforeParams = isIn ? internalParams : that.margeData(outParams, internalParams);// 处理动态参数const url = that.withParam(beforeUrl, beforeParams);// 请求方法return axiosInstance({url,method,...reuqestConfig})}}// 合并数据 内部参数优先于外部参数// 如果key相同替换掉外部参数private margeData(outData: any, innerData: any ): any { return Object.assign({}, outData, innerData);};// 处理params参数private withParam(url: string, params: any): string { // 获取所有keylet keys = Object.keys(params);// 如果没有值直接返回原有 url 地址if (!keys.length) return url;// 替换特殊标识符// url/{id}/{age} // params对象 {id:1,age:18}// 处理后 url/1/18return url.replace(/\{(\w+)\}/gi, ($1, $2) => {return params[$2] || "";});};
} 

上述配置中一直提到的onlyIn这个配置参数,有的时候可能在请求的时候可能使用FormData对象,但是一旦使用对象对象合并处理之后FormData就会失效,所以我们需要只使用调用方法时所传入的参数,onlyIn即代表是否只使用内部参数。

完成上述代码就完成了所有功能,和最初的需求相对比就已经完成了所有的功能,接下来就需要实践一下所封装方法工厂是否可用。

实例化工厂函数:

// api/index.ts
import Request from "utils/AxiosFactory/index";
import type {AxiosRequestConfig,AxiosResponse,AxiosError
} from "axios";
const request = new Request({baseURL: "https://autumnfish.cn/"
});
const requestIntercept = (config: AxiosRequestConfig): AxiosRequestConfig => {console.log("请求拦截");return config;
};
const responseIntercept = (response: AxiosResponse): AxiosResponse => {console.log("响应拦截");return response;
};
const processingError = (error: AxiosError): AxiosError => {console.log("出错了");return error;
};
request.interceptors({requestIntercept,requestError: processingError,responseIntercept,responseError: processingError
});
export default request; 

使用:

<!-- Vue3.0 -->
<template><div><ul class="m-[10px]"><li class="border-solid divide-y border-[1px] rounded-[4px] text-center"v-for="(song) of state.songs":key="song.id">{{ song.name }}</li></ul></div>
</template>

<script lang="ts" setup> import axios from "api/index";
import { reactive, onMounted } from "vue";
const state: {songs: any[]
} = reactive({songs: []
});

axios.setConfig("test", [{key: "getAbc",url: "/search",method: "get",query: {abc: 1}
}]);
const methods = axios.create("test");

onMounted(async () => { const { data } = await methods.getAbc({query: { keywords: "北京" }});const { result } = data;state.songs = result.songs || [{ name: "北京" }];
}) </script> 

我们的配置收集可以单独的放到一个文件中,在文件中统计进行收集,如果不想重复做类似的操作的的话可以使用文件扫描,一次性收集到所有的请求信息。我们只需要在调用的时候使用创建所需要的方法即可。

本次封装虽然仍会存在一些问题,比如上传文件,特定方法axios设置header等。。。如果想实现这些也是可以的需要对代码进行微调整即可。

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值