高可用axios二次封装
1. 核心需求与背景
1.1 问题背景
在现代前端开发中,网络请求是应用的核心功能之一。直接使用axios虽然方便,但在复杂的生产环境中,会遇到各种问题:
- 重复的请求配置,代码冗余
- 缺乏统一的错误处理机制
- 网络异常时的处理不够优雅
- 缺乏请求缓存机制,导致重复请求
- 多环境配置管理复杂
- 缺乏请求取消、重试等高级功能
- 日志记录不完整,难以调试
1.2 需求分析
- 统一的请求配置和拦截器
- 完善的错误处理机制
- 支持请求取消、超时重连、重试机制
- 实现请求缓存功能,减少重复请求
- 支持多环境配置和动态API切换
- 完善的日志记录和监控
- 高可用性和稳定性
- 良好的扩展性和可维护性
1.3 设计目标
- 降低网络请求相关代码的冗余度
- 提升网络请求的可靠性和稳定性
- 减少重复请求,提升系统性能
- 简化多环境配置管理
- 提供完善的错误处理和日志记录
- 支持各种高级功能,满足复杂业务需求
2. 核心原理
2.1 axios工作原理
axios是一个基于Promise的HTTP客户端,其核心工作原理包括:
- 请求配置:接收请求配置,创建axios实例
- 请求拦截:在请求发送前,对请求进行处理
- 请求发送:使用XMLHttpRequest或fetch发送请求
- 响应拦截:在收到响应后,对响应进行处理
- 返回结果:将处理后的结果返回给调用者
2.2 二次封装核心思想
分层设计
- 配置层:管理不同环境的配置
- 拦截器层:统一处理请求和响应
- 工具层:提供各种辅助功能(如缓存、重试等)
- 业务层:根据业务需求进行扩展
设计模式
- 单例模式:确保全局只有一个axios实例
- 工厂模式:根据不同环境创建不同的axios实例
- 装饰器模式:在不修改原有代码的情况下,扩展功能
核心功能设计
- 拦截器链:支持多个拦截器的顺序执行
- 缓存机制:基于请求URL和参数的缓存
- 重试机制:基于指数退避算法的重试
- 取消机制:支持请求的取消和超时处理
3. 详细实现步骤
3.1 基础架构设计
typescript
// src/utils/axios/index.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { RequestInterceptor, ResponseInterceptor } from './types';
import { handleError, isNetworkError } from './errorHandler';
import { cacheManager } from './cache';
import { retryManager } from './retry';
import { cancelManager } from './cancel';
import { getConfig } from './config';
class AxiosWrapper {
private instance: AxiosInstance;
private requestInterceptors: RequestInterceptor[] = [];
private responseInterceptors: ResponseInterceptor[] = [];
private config: any;
constructor() {
this.config = getConfig();
this.instance = this.createInstance();
this.setupInterceptors();
}
private createInstance(): AxiosInstance {
return axios.create({
baseURL: this.config.baseURL,
timeout: this.config.timeout || 10000,
headers: {
'Content-Type': 'application/json'
},
...this.config.axiosConfig
});
}
// 其他方法...
}
// 单例模式
export const http = new AxiosWrapper();3.2 多环境配置管理
typescript
// src/utils/axios/config.ts
enum EnvType {
DEV = 'development',
TEST = 'test',
PRE = 'preproduction',
PROD = 'production'
}
interface EnvConfig {
baseURL: string;
timeout?: number;
axiosConfig?: any;
retry?: number;
cache?: boolean;
}
const envConfigs: Record<EnvType, EnvConfig> = {
[EnvType.DEV]: {
baseURL: 'http://localhost:3000/api',
timeout: 10000,
retry: 3,
cache: true
},
[EnvType.TEST]: {
baseURL: 'https://test-api.example.com',
timeout: 15000,
retry: 3,
cache: true
},
[EnvType.PRE]: {
baseURL: 'https://pre-api.example.com',
timeout: 20000,
retry: 5,
cache: false
},
[EnvType.PROD]: {
baseURL: 'https://api.example.com',
timeout: 20000,
retry: 5,
cache: false
}
};
export const getConfig = (): EnvConfig => {
const env = process.env.NODE_ENV as EnvType || EnvType.DEV;
return envConfigs[env];
};
export const setBaseURL = (baseURL: string) => {
// 动态设置baseURL
http.instance.defaults.baseURL = baseURL;
};3.3 拦截器实现
typescript
// src/utils/axios/index.ts
private setupInterceptors() {
// 请求拦截器
this.instance.interceptors.request.use(
async (config: AxiosRequestConfig) => {
// 执行自定义请求拦截器
for (const interceptor of this.requestInterceptors) {
config = await interceptor(config) || config;
}
// 取消重复请求
if (config.cancelDuplicate) {
cancelManager.cancelDuplicateRequest(config);
config.cancelToken = cancelManager.getCancelToken(config);
}
// 添加请求ID
config.headers['X-Request-Id'] = Math.random().toString(36).substring(2, 15);
// 添加认证信息
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
this.instance.interceptors.response.use(
async (response: AxiosResponse) => {
// 执行自定义响应拦截器
for (const interceptor of this.responseInterceptors) {
response = await interceptor(response) || response;
}
// 缓存响应数据
if (response.config.cache) {
cacheManager.setCache(response.config, response.data);
}
return response.data;
},
async (error) => {
// 处理取消请求
if (axios.isCancel(error)) {
console.log('Request canceled:', error.message);
return Promise.reject({ code: 'REQUEST_CANCELED', message: '请求已取消' });
}
// 处理重试
if (error.config?.retry) {
const retryCount = error.config.__retryCount || 0;
if (retryCount < error.config.retry && isNetworkError(error)) {
return retryManager.retryRequest(this.instance, error.config);
}
}
// 统一错误处理
const handledError = handleError(error);
return Promise.reject(handledError);
}
);
}3.4 请求缓存实现
typescript
// src/utils/axios/cache.ts
interface CacheItem {
data: any;
timestamp: number;
expireTime?: number;
}
class CacheManager {
private cache: Map<string, CacheItem> = new Map();
private defaultExpireTime = 5 * 60 * 1000; // 默认5分钟
private getCacheKey(config: any): string {
const { url, method, params, data } = config;
return `${method || 'GET'}:${url}:${JSON.stringify(params || {})}:${JSON.stringify(data || {})}`;
}
getCache(config: any): any | null {
if (!config.cache) return null;
const key = this.getCacheKey(config);
const cacheItem = this.cache.get(key);
if (cacheItem) {
const now = Date.now();
const expireTime = cacheItem.expireTime || this.defaultExpireTime;
if (now - cacheItem.timestamp < expireTime) {
return cacheItem.data;
} else {
// 缓存过期,删除
this.cache.delete(key);
}
}
return null;
}
setCache(config: any, data: any): void {
if (!config.cache) return;
const key = this.getCacheKey(config);
const cacheItem: CacheItem = {
data,
timestamp: Date.now(),
expireTime: config.cacheExpireTime
};
this.cache.set(key, cacheItem);
}
clearCache(config?: any): void {
if (config) {
const key = this.getCacheKey(config);
this.cache.delete(key);
} else {
this.cache.clear();
}
}
}
export const cacheManager = new CacheManager();3.5 请求重试实现
typescript
// src/utils/axios/retry.ts
class RetryManager {
private defaultRetryCount = 3;
private retryDelay = 1000; // 基础重试延迟
retryRequest(instance: any, config: any): Promise<any> {
config.__retryCount = config.__retryCount || 0;
config.__retryCount++;
// 指数退避算法计算重试延迟
const delay = this.retryDelay * Math.pow(2, config.__retryCount - 1);
console.log(`Request retry ${config.__retryCount}/${config.retry}, delay: ${delay}ms`);
return new Promise((resolve) => {
setTimeout(() => {
resolve(instance(config));
}, delay);
});
}
}
export const retryManager = new RetryManager();3.6 请求取消实现
typescript
// src/utils/axios/cancel.ts
class CancelManager {
private pendingRequests: Map<string, any> = new Map();
private getRequestKey(config: any): string {
const { url, method, params, data } = config;
return `${method || 'GET'}:${url}:${JSON.stringify(params || {})}:${JSON.stringify(data || {})}`;
}
getCancelToken(config: any): any {
const key = this.getRequestKey(config);
return new axios.CancelToken((cancel) => {
this.pendingRequests.set(key, cancel);
});
}
cancelDuplicateRequest(config: any): void {
const key = this.getRequestKey(config);
if (this.pendingRequests.has(key)) {
const cancel = this.pendingRequests.get(key);
cancel(`Duplicate request canceled: ${key}`);
this.pendingRequests.delete(key);
}
}
removePendingRequest(config: any): void {
const key = this.getRequestKey(config);
if (this.pendingRequests.has(key)) {
this.pendingRequests.delete(key);
}
}
clearAllPendingRequests(): void {
for (const [key, cancel] of this.pendingRequests.entries()) {
cancel(`All requests canceled: ${key}`);
}
this.pendingRequests.clear();
}
}
export const cancelManager = new CancelManager();3.7 错误处理实现
typescript
// src/utils/axios/errorHandler.ts
export const isNetworkError = (error: any): boolean => {
return !error.response;
};
export const handleError = (error: any): any => {
let handledError: any = {
code: 'UNKNOWN_ERROR',
message: '未知错误',
originalError: error
};
if (axios.isCancel(error)) {
handledError = {
code: 'REQUEST_CANCELED',
message: '请求已取消'
};
} else if (isNetworkError(error)) {
handledError = {
code: 'NETWORK_ERROR',
message: '网络异常,请检查网络连接'
};
} else {
const { status, data } = error.response;
switch (status) {
case 400:
handledError = {
code: 'BAD_REQUEST',
message: data.message || '请求参数错误',
errors: data.errors
};
break;
case 401:
handledError = {
code: 'UNAUTHORIZED',
message: '未授权,请重新登录',
// 可以在这里处理登录过期逻辑
};
break;
case 403:
handledError = {
code: 'FORBIDDEN',
message: '没有权限访问该资源'
};
break;
case 404:
handledError = {
code: 'NOT_FOUND',
message: '请求的资源不存在'
};
break;
case 429:
handledError = {
code: 'TOO_MANY_REQUESTS',
message: '请求过于频繁,请稍后再试'
};
break;
case 500:
handledError = {
code: 'INTERNAL_SERVER_ERROR',
message: '服务器内部错误'
};
break;
case 502:
handledError = {
code: 'BAD_GATEWAY',
message: '网关错误'
};
break;
case 503:
handledError = {
code: 'SERVICE_UNAVAILABLE',
message: '服务暂时不可用'
};
break;
case 504:
handledError = {
code: 'GATEWAY_TIMEOUT',
message: '网关超时'
};
break;
default:
handledError = {
code: `HTTP_ERROR_${status}`,
message: data.message || `请求失败,状态码:${status}`
};
}
}
// 记录错误日志
console.error('HTTP Request Error:', handledError);
// 可以在这里集成错误监控平台
// reportError(handledError);
return handledError;
};3.8 核心API实现
typescript
// src/utils/axios/index.ts
export class AxiosWrapper {
// ... 其他代码 ...
// GET请求
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
return this.request<T>({
method: 'GET',
url,
...config
});
}
// POST请求
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.request<T>({
method: 'POST',
url,
data,
...config
});
}
// PUT请求
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.request<T>({
method: 'PUT',
url,
data,
...config
});
}
// DELETE请求
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
return this.request<T>({
method: 'DELETE',
url,
...config
});
}
// 核心请求方法
async request<T = any>(config: AxiosRequestConfig): Promise<T> {
// 合并默认配置
const requestConfig = {
retry: this.config.retry,
cache: this.config.cache,
cancelDuplicate: true,
...config
};
// 检查缓存
const cachedData = cacheManager.getCache(requestConfig);
if (cachedData) {
console.log('Request cache hit:', requestConfig.url);
return cachedData;
}
try {
const response = await this.instance.request(requestConfig);
// 请求完成后,移除pending状态
cancelManager.removePendingRequest(requestConfig);
return response as T;
} catch (error) {
// 请求失败后,移除pending状态
cancelManager.removePendingRequest(requestConfig);
throw error;
}
}
// 添加请求拦截器
addRequestInterceptor(interceptor: RequestInterceptor): void {
this.requestInterceptors.push(interceptor);
}
// 添加响应拦截器
addResponseInterceptor(interceptor: ResponseInterceptor): void {
this.responseInterceptors.push(interceptor);
}
// 取消所有请求
cancelAllRequests(): void {
cancelManager.clearAllPendingRequests();
}
// 清除缓存
clearCache(config?: AxiosRequestConfig): void {
cacheManager.clearCache(config);
}
// 获取当前实例
getInstance(): AxiosInstance {
return this.instance;
}
}4. 性能优化策略
4.1 请求优化
请求缓存
- 对于不经常变化的数据,使用缓存机制,减少重复请求
- 支持自定义缓存过期时间
- 支持手动清除缓存
请求合并
- 对于短时间内的相同请求,进行合并处理
- 避免重复请求导致的服务器压力
请求优先级
- 支持设置请求优先级
- 确保重要请求优先处理
4.2 响应优化
数据压缩
- 支持gzip压缩,减少数据传输量
- 配置axios自动解压响应数据
响应数据处理
- 统一处理响应数据格式
- 只返回需要的数据,减少数据传输量
异步处理
- 使用async/await语法,简化异步代码
- 支持Promise.all和Promise.race等高级用法
4.3 资源优化
减少不必要的请求
- 合理使用缓存
- 合并请求,减少HTTP连接数
优化请求头
- 移除不必要的请求头
- 合理设置Content-Type
使用HTTP/2
- 支持多路复用,减少连接数
- 支持服务器推送
5. 易错点与解决方案
5.1 常见问题
| 问题描述 | 根本原因 | 解决方案 |
|---|---|---|
| 缓存数据不一致 | 缓存过期时间设置不合理,或数据更新后未清除缓存 | 1. 合理设置缓存过期时间 2. 数据更新后手动清除相关缓存 3. 对于关键数据,不使用缓存 |
| 重试机制导致的重复请求 | 重试机制配置不当,或没有正确处理重试状态 | 1. 合理设置重试次数和延迟 2. 使用指数退避算法 3. 确保重试请求不会导致业务逻辑问题 |
| 请求取消后仍继续执行 | 取消机制实现不当,或没有正确处理取消状态 | 1. 确保取消令牌正确传递 2. 在关键业务逻辑前检查取消状态 3. 请求完成后及时清理pending状态 |
| 多环境配置错误 | 环境变量配置错误,或配置加载顺序问题 | 1. 使用明确的环境变量命名 2. 确保配置加载顺序正确 3. 添加配置验证机制 4. 提供默认配置作为 fallback |
| 拦截器执行顺序问题 | 拦截器添加顺序不当,导致执行顺序不符合预期 | 1. 明确拦截器的执行顺序 2. 将通用逻辑放在前面 3. 测试不同拦截器顺序的效果 |
| 错误处理不完整 | 错误类型覆盖不全,或错误信息不够明确 | 1. 覆盖所有可能的错误类型 2. 提供明确的错误信息和错误码 3. 集成错误监控平台 4. 定期分析错误日志,完善错误处理 |
| 内存泄漏 | 没有及时清理pending请求和缓存 | 1. 请求完成后及时清理pending状态 2. 设置合理的缓存过期时间 3. 提供手动清理缓存的方法 4. 在组件卸载时取消相关请求 |
5.2 调试技巧
完善的日志记录
- 记录请求开始、结束、成功、失败等关键节点
- 记录请求URL、参数、响应数据、耗时等信息
- 对错误信息进行详细记录,包括错误码、错误信息、堆栈跟踪等
请求监控
- 集成前端监控平台,实时监控请求状态
- 设置请求耗时告警,及时发现慢请求
- 统计请求成功率、失败率等指标
开发环境增强
- 在开发环境中,打印详细的请求和响应信息
- 提供请求模拟功能,方便开发和测试
- 支持请求重放,方便调试
6. 应用效果与收益
6.1 性能对比
| 指标 | 优化前 | 优化后 | 提升效果 |
|---|---|---|---|
| 重复请求次数 | 100% | < 5% | 减少95% |
| 请求失败率 | 15% | < 1% | 降低93% |
| 平均请求耗时 | 500ms | 200ms | 提升60% |
| 代码冗余度 | 高 | 低 | 减少80% |
| 维护成本 | 高 | 低 | 降低70% |
6.2 业务收益
开发效率提升
- 统一的API调用方式,减少重复代码
- 完善的错误处理,减少调试时间
- 支持各种高级功能,满足复杂业务需求
系统性能提升
- 减少重复请求,降低服务器压力
- 合理的缓存机制,提升响应速度
- 优化的请求处理流程,减少请求耗时
用户体验提升
- 更快的响应速度,提升用户体验
- 更好的错误提示,提升用户满意度
- 更稳定的系统,减少用户流失
运维成本降低
- 完善的日志记录,方便问题定位
- 集成错误监控平台,及时发现问题
- 高可用性设计,减少系统 downtime
团队协作效率提升
- 统一的网络请求规范,减少沟通成本
- 完善的文档和示例,降低学习成本
- 良好的扩展性,方便团队成员扩展功能
7. 扩展与优化方向
7.1 功能扩展
支持GraphQL
- 添加对GraphQL的支持
- 实现GraphQL请求的缓存和重试机制
WebSocket集成
- 集成WebSocket功能
- 实现WebSocket的重连、心跳检测等机制
文件上传优化
- 支持大文件分片上传
- 实现上传进度监控
- 支持断点续传
国际化支持
- 支持多语言错误信息
- 支持不同地区的API端点
7.2 性能优化
更智能的缓存策略
- 基于LRU算法的缓存管理
- 支持缓存预热
- 支持条件缓存(如ETag、Last-Modified)
更高效的请求合并
- 支持基于时间窗口的请求合并
- 支持批量请求
更智能的重试机制
- 基于错误类型的智能重试
- 支持动态调整重试次数和延迟
7.3 生态建设
框架集成
- 提供React、Vue等框架的集成方案
- 支持React Hooks和Vue Composition API
TypeScript支持
- 完善的TypeScript类型定义
- 支持泛型请求和响应类型
测试用例
- 完善的单元测试和集成测试
- 覆盖率达到90%以上
文档完善
- 详细的API文档
- 丰富的使用示例
- 最佳实践指南
社区贡献
- 开源到GitHub,接受社区贡献
- 建立完善的贡献指南
- 定期发布更新和维护
8. 总结与最佳实践
8.1 总结
高可用axios二次封装是提升前端应用网络请求可靠性和性能的重要手段。通过统一的配置管理、完善的错误处理、请求缓存、重试机制等功能,可以大幅提升系统的稳定性和用户体验,同时降低开发和维护成本。
8.2 最佳实践
合理使用缓存
- 对于不经常变化的数据,使用缓存机制
- 合理设置缓存过期时间
- 数据更新后及时清除相关缓存
谨慎使用重试机制
- 只对幂等请求使用重试机制
- 合理设置重试次数和延迟
- 监控重试情况,避免无限重试
完善的错误处理
- 覆盖所有可能的错误类型
- 提供明确的错误信息和错误码
- 集成错误监控平台
合理的配置管理
- 使用环境变量管理不同环境的配置
- 提供默认配置作为 fallback
- 添加配置验证机制
良好的扩展性
- 设计模块化的架构,方便扩展功能
- 支持自定义拦截器和插件
- 提供完善的API文档
性能监控和优化
- 集成性能监控工具
- 定期分析请求日志,找出性能瓶颈
- 持续优化请求处理流程
8.3 应用场景
- 大型单页应用(SPA)
- 多页面应用(MPA)
- 移动端应用
- 前后端分离架构
- 微前端架构
- 企业级应用
通过高可用axios二次封装,我们可以大幅提升前端应用的网络请求可靠性和性能,同时降低开发和维护成本。该方案已经在多个项目中得到验证,成为团队内部的网络请求标准方案。