背景
随着项目规模增大,如果每发起一次HTTP请求,就要把这些比如设置超时时间、设置请求头、根据项目环境判断使用哪个请求地址、错误处理等等操作,都需要写一遍,这种重复劳动不仅浪费时间,而且让代码变得冗余不堪,难以维护。总的来说,对 axios 进行二次封装有如下好处:
- 代码封装,重用性高,减少代码量,减少维护难度
- 统一处理一些常规的问题,一劳永逸,比如 HTTP 错误
- 拦截请求和响应,提前对数据进行处理,如获取 TOKEN,修改配置项
封装要考虑的问题
- 根据开发、测试、生产环境的不同,接口请求前缀需要加以区分
- 请求之前处理 config
- 根据接口返回的不同状态码做不同的处理
- 对 Get、Post 等方法进行封装,使用起来更方便
- 针对文件上传封装统一的请求方法
- 在响应拦截器中进行错误捕获
- 具备取消重复请求、错误请求重连的功能
- 对接口返回的数据进行处理,封装消息提示方法
重点细节复盘
1、重复请求问题
export class AxiosCanceler {
/**
* 添加请求
* @param {Object} config
*/
addPending(config: AxiosRequestConfig) {
this.removePending(config);
const url = getPendingUrl(config);
config.cancelToken =
config.cancelToken ||
new axios.CancelToken((cancel) => {
if (!pendingMap.has(url)) {
// 如果 pending 中不存在当前请求,则添加进去
pendingMap.set(url, cancel);
}
});
}
/**
* @description: 清空所有pending
*/
removeAllPending() {
pendingMap.forEach((cancel) => {
cancel && isFunction(cancel) && cancel();
});
pendingMap.clear();
}
/**
* 移除请求
* @param {Object} config
*/
removePending(config: AxiosRequestConfig) {
const url = getPendingUrl(config);
if (pendingMap.has(url)) {
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
const cancel = pendingMap.get(url);
cancel && cancel(url);
pendingMap.delete(url);
}
}
/**
* @description: 重置
*/
reset(): void {
pendingMap = new Map<string, Canceler>();
}
从 v0.22.0 开始,Axios 支持以 fetch API 方式—— AbortController 取消请求
取消请求 | Axios Docs
小结
- 为每个请求生成唯一标识:通过
getPendingUrl确保每个请求可以唯一标识。 - 添加请求到
pendingMap: - 在请求发起前,生成取消令牌并存入
pendingMap。 - 移除已存在的重复请求:在
addPending中调用removePending,确保当前的请求是最新的,避免重复发送。 - 支持取消所有请求:提供
removeAllPending方法,便于全局清理。 - 重置请求管理器:通过
reset方法清空所有记录。
2、在 axios 实例类中封装请求方法(Get、Post、Put、Delete) 好处:不用写重复代码,只需要维护对应的api路径
get<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
return this.request({ ...config, method: 'GET' }, options);
}
post<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
return this.request({ ...config, method: 'POST' }, options);
}
put<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
return this.request({ ...config, method: 'PUT' }, options);
}
delete<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
return this.request({ ...config, method: 'DELETE' }, options);
}
设计模式
-
单例模式 (Singleton Pattern)
pendingMap是一个全局唯一的实例,用于存储和管理所有的请求标识和取消函数。通过单例模式,确保请求管理器在整个应用中只有一个统一的数据管理对象。 -
策略模式 (Strategy Pattern)
AxiosCanceler类中的方法(如addPending、removePending、removeAllPending)可以视为不同的策略,用于管理请求的生命周期。根据需求,调用不同的方法实现对应的功能:addPending:用于添加新的请求。removePending:用于移除指定请求。removeAllPending:用于清空所有请求。reset:重置请求管理器。
参数的加密解密
base64加密正常是字符串(String类型)进行加密,对对象(Object)进行加密,需要通过JSON.parse进行转义成String类型
/**
* base64(解密)
* @param {String} str 跳转参数为base64字符串
* @returns
*/
export const decryptBase64 = function(str) {
// 添加decodeURIComponent解决其他特殊字符,如等号(=)会转成%3D,导致base64解密失败
const decryptQuery = str ? JSON.parse(base64.decode(decodeURIComponent(str))) : {}
return decryptQuery
}
/**
* base64(加密)
* @param {Object || String} param 跳转参数可以为对象或路径字符串
* @returns
*/
export const encryptBase64 = function(param) {
const encryptStr = base64.encode(JSON.stringify(param)) || ''
return encryptStr
}