在处理多后端地址且拦截器逻辑各不相同的项目时,核心在于实现配置隔离 和逻辑解耦。通过创建多个独立的Axios实例,并为每个实例配置专属的拦截器和默认设置,是当前最受推荐的做法。 以下是一种稳健的封装方案,你可以根据项目规模进行调整。
💡 核心封装策略
1. 创建多个Axios实例
为每个后端服务创建独立的Axios实例,这是实现配置隔离的基础。
arduino
// src/utils/request.js
import axios from 'axios';
// 创建主业务API实例
export const mainAPI = axios.create({
baseURL: process.env.VUE_APP_MAIN_API, // 从环境变量读取
timeout: 10000,
});
// 创建第三方服务API实例(例如企业资源计划系统)
export const erpAPI = axios.create({
baseURL: process.env.VUE_APP_ERP_API,
timeout: 15000, // 该服务响应较慢,设置更长超时时间
});
// 创建文件上传服务实例
export const uploadAPI = axios.create({
baseURL: process.env.VUE_APP_UPLOAD_API,
timeout: 30000, // 文件上传需要更长时间
headers: { 'Content-Type': 'multipart/form-data' }, // 专属请求头
});
2. 为每个实例配置独立的拦截器
针对每个实例的特定需求,设置专属的请求和响应拦截器。
javascript
// 主业务API拦截器
mainAPI.interceptors.request.use(
(config) => {
// 自动添加认证Token
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
mainAPI.interceptors.response.use(
(response) => {
// 统一处理主业务后端的数据返回格式
if (response.data.code === 200) {
return response.data;
} else {
// 处理业务逻辑错误
alert(response.data.message);
return Promise.reject(new Error(response.data.message));
}
},
(error) => {
// 统一处理HTTP错误状态码
if (error.response?.status === 401) {
alert('登录已过期,请重新登录');
// 清除token并跳转到登录页
localStorage.removeItem('access_token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
// 第三方服务拦截器(逻辑可能完全不同)
erpAPI.interceptors.request.use(
(config) => {
// 第三方服务可能使用不同的认证方式,例如API Key
config.headers['API-Key'] = process.env.VUE_APP_ERP_API_KEY;
return config;
},
(error) => Promise.reject(error)
);
erpAPI.interceptors.response.use(
(response) => {
// 第三方服务的响应结构可能不同,直接返回数据
return response;
},
(error) => {
// 对第三方服务的错误有特殊处理
console.error('ERP接口调用失败:', error);
return Promise.reject(error);
}
);
3. 模块化封装与统一导出
将不同业务的API请求函数按模块分类,便于维护和管理。
javascript
// src/api/main.js (主业务API模块)
import { mainAPI } from '@/utils/request';
export const userAPI = {
login: (data) => mainAPI.post('/user/login', data),
getProfile: () => mainAPI.get('/user/profile'),
updateProfile: (data) => mainAPI.put('/user/profile', data),
};
export const orderAPI = {
list: (params) => mainAPI.get('/orders', { params }),
detail: (id) => mainAPI.get(`/orders/${id}`),
create: (data) => mainAPI.post('/orders', data),
};
javascript
// src/api/erp.js (ERP系统API模块)
import { erpAPI } from '@/utils/request';
export const erp = {
getInventory: (params) => erpAPI.get('/inventory', { params }),
updateStock: (data) => erpAPI.post('/stock/update', data),
};
4. 高级优化技巧
- 防止重复请求:在拦截器中实现取消重复请求的逻辑。
- 环境差异化配置:结合环境变量,为开发、测试、生产环境配置不同的基础地址。
- 请求重试机制:为不稳定的服务添加有限的自动重试功能。
📝 实践示例
在实际组件中使用封装好的API:
javascript
// 在Vue组件中使用
import { userAPI } from '@/api/main';
import { erp } from '@/api/erp';
export default {
methods: {
async handleLogin() {
try {
const result = await userAPI.login({
username: 'admin',
password: 'password'
});
// 处理登录结果
} catch (error) {
// 错误已由拦截器统一处理,这里可根据需要做UI层面的特殊处理
this.$message.error('登录失败');
}
},
async loadInventory() {
try {
const inventory = await erp.getInventory({ warehouse: 'default' });
this.inventoryData = inventory;
} catch (error) {
// ERP接口错误处理
}
}
}
}
💎 封装的价值
通过这种封装方式,你获得了:
- 高度隔离:各后端服务配置互不干扰。
- 维护性强:相关变更影响范围小,易于调试和迭代。
- 开发体验好:业务代码简洁明了,只需关注业务逻辑。
- 灵活性高:轻松应对各后端服务的个性化需求。
这种多实例方案虽然初始设置稍显复杂,但随着项目发展,其优势会愈发明显,特别适合中大型前端项目。 希望这份详细的方案能帮助你更好地组织代码。如果你有更具体的场景或疑问,我可以提供更具针对性的建议。