1. 基础封装
1.1 类型定义 (TypeScript)
types/api.d.ts
export interface APIConfig {
baseURL: string;
globalHeaders?: Record<string, string>;
modules: Record<string, APIModule>;
timeout?: number;
}
export interface APIModule {
basePath: string;
apis: APIEndpoint[];
}
export interface APIEndpoint {
name: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
url: string;
mock?: any;
}
export interface APIParams {
pathParams?: Record<string, string | number>;
queryParams?: Record<string, any>;
body?: any;
headers?: Record<string, string>;
}
export interface APIResponse<T = any> {
code: number;
message: string;
data: T;
timestamp: number;
}
export type RequestInterceptor = (config: any) => any;
export type ResponseInterceptor = (response: any) => any;
export type ErrorInterceptor = (error: any) => Promise<any>;
1.2 Axios 封装核心类
utils/api-manager.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
import { APIConfig, APIModule, APIEndpoint, APIParams, APIResponse, RequestInterceptor, ResponseInterceptor, ErrorInterceptor } from '@/types/api';
class APIManager {
private axiosInstance: AxiosInstance;
private config: APIConfig;
private modules: Record<string, any> = {};
private requestInterceptors: RequestInterceptor[] = [];
private responseInterceptors: ResponseInterceptor[] = [];
private errorInterceptors: ErrorInterceptor[] = [];
constructor(config: APIConfig) {
this.config = config;
this.axiosInstance = axios.create({
baseURL: config.baseURL,
timeout: config.timeout || 10000,
headers: config.globalHeaders || {}
});
this.setupInterceptors();
this.initializeModules();
}
private setupInterceptors() {
this.axiosInstance.interceptors.request.use(
(config) => {
this.requestInterceptors.forEach(interceptor => {
config = interceptor(config);
});
return config;
},
(error) => {
return Promise.reject(error);
}
);
this.axiosInstance.interceptors.response.use(
(response) => {
this.responseInterceptors.forEach(interceptor => {
response = interceptor(response);
});
return response;
},
(error) => {
return this.handleError(error);
}
);
}
private async handleError(error: AxiosError): Promise<any> {
let processedError = error;
for (const interceptor of this.errorInterceptors) {
try {
processedError = await interceptor(processedError);
} catch (e) {
console.error('Error interceptor failed:', e);
}
}
return Promise.reject(processedError);
}
private initializeModules() {
Object.keys(this.config.modules).forEach(moduleName => {
this.modules[moduleName] = this.createModule(moduleName);
});
}
private createModule(moduleName: string): any {
const moduleConfig = this.config.modules[moduleName];
const module: any = {};
moduleConfig.apis.forEach(api => {
module[api.name] = (params: APIParams = {}, options: AxiosRequestConfig = {}) => {
return this.createAPICall(moduleConfig.basePath, api, params, options);
};
});
return module;
}
private async createAPICall(
basePath: string,
api: APIEndpoint,
params: APIParams,
options: AxiosRequestConfig
): Promise<APIResponse> {
if (process.env.NODE_ENV === 'development' && api.mock) {
console.warn(`Using mock data for ${api.name}`);
return Promise.resolve(api.mock);
}
let url = `${basePath}${api.url}`;
if (params.pathParams) {
Object.keys(params.pathParams).forEach(key => {
url = url.replace(`:${key}`, encodeURIComponent(params.pathParams![key]));
});
}
if (params.queryParams) {
const queryString = new URLSearchParams(params.queryParams).toString();
url += `?${queryString}`;
}
const finalConfig: AxiosRequestConfig = {
method: api.method,
url,
headers: {
...params.headers,
...options.headers
},
...options
};
if (['POST', 'PUT', 'PATCH'].includes(api.method) {
finalConfig.data = params.body;
}
try {
const response = await this.axiosInstance.request(finalConfig);
return response.data;
} catch (error) {
throw error;
}
}
public addRequestInterceptor(interceptor: RequestInterceptor): void {
this.requestInterceptors.push(interceptor);
}
public addResponseInterceptor(interceptor: ResponseInterceptor): void {
this.responseInterceptors.push(interceptor);
}
public addErrorInterceptor(interceptor: ErrorInterceptor): void {
this.errorInterceptors.push(interceptor);
}
public getModule<T = any>(moduleName: string): T {
if (!this.modules[moduleName]) {
throw new Error(`Module ${moduleName} not found`);
}
return this.modules[moduleName];
}
}
export default APIManager;
1.3 API 配置示例
config/api-config.ts
import { APIConfig } from '@/types/api';
const apiConfig: APIConfig = {
baseURL: process.env.VUE_APP_API_BASE_URL || '/api',
timeout: 15000,
globalHeaders: {
'Content-Type': 'application/json'
},
modules: {
user: {
basePath: '/user',
apis: [
{
name: 'login',
method: 'POST',
url: '/login',
mock: { code: 200, message: 'success', data: { token: 'mock-token' }, timestamp: Date.now() }
},
{
name: 'getUserInfo',
method: 'GET',
url: '/info/:id',
mock: { code: 200, message: 'success', data: { id: 1, name: 'Mock User' }, timestamp: Date.now() }
},
{
name: 'updateUser',
method: 'PUT',
url: '/update'
}
]
},
product: {
basePath: '/product',
apis: [
{
name: 'getProductList',
method: 'GET',
url: '/list'
},
{
name: 'getProductDetail',
method: 'GET',
url: '/detail/:id'
}
]
}
}
};
export default apiConfig;
1.4 初始化实例
utils/api.ts
import APIManager from './api-manager';
import apiConfig from '../config/api-config';
const apiManager = new APIManager(apiConfig);
apiManager.addRequestInterceptor((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers = config.headers || {};
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
apiManager.addResponseInterceptor((response) => {
if (response.data && response.data.code !== 200) {
console.error(`API Error: ${response.data.message}`);
}
return response;
});
apiManager.addErrorInterceptor((error) => {
if (error.response && error.response.status === 401) {
window.location.href = '/login';
}
return Promise.reject(error);
});
export default apiManager;
2. 使用示例
2.1 基本使用
import apiManager from '@/utils/api';
const userModule = apiManager.getModule<{
login: (params: { body: { username: string; password: string } }) => Promise<APIResponse<{ token: string }>>;
getUserInfo: (params: { pathParams: { id: number } }) => Promise<APIResponse<{ id: number; name: string }>>;
updateUser: (params: { body: { id: number; name: string } }) => Promise<APIResponse>;
}>('user');
const productModule = apiManager.getModule<{
getProductList: (params?: { queryParams?: { page: number; size: number } }) => Promise<APIResponse<any[]>>;
getProductDetail: (params: { pathParams: { id: number } }) => Promise<APIResponse<any>>;
}>('product');
async function exampleUsage() {
try {
const loginRes = await userModule.login({
body: {
username: 'admin',
password: '123456'
}
});
console.log('Login success:', loginRes.data.token);
const userInfo = await userModule.getUserInfo({
pathParams: { id: 1 }
});
console.log('User info:', userInfo.data);
const products = await productModule.getProductList({
queryParams: { page: 1, size: 10 }
});
console.log('Product list:', products.data);
const productDetail = await productModule.getProductDetail({
pathParams: { id: 123 }
});
console.log('Product detail:', productDetail.data);
} catch (error) {
console.error('API error:', error);
}
}
2.2 在 Vue 中使用
import { ref } from 'vue';
import apiManager from '@/utils/api';
import type { APIResponse } from '@/types/api';
export function useApi() {
const loading = ref(false);
const error = ref<any>(null);
const userModule = apiManager.getModule('user');
const productModule = apiManager.getModule('product');
const login = async (username: string, password: string) => {
loading.value = true;
error.value = null;
try {
const res = await userModule.login({
body: { username, password }
});
return res.data;
} catch (err) {
error.value = err;
throw err;
} finally {
loading.value = false;
}
};
const getProductList = async (page: number = 1, size: number = 10) => {
loading.value = true;
try {
const res = await productModule.getProductList({
queryParams: { page, size }
});
return res.data;
} catch (err) {
error.value = err;
throw err;
} finally {
loading.value = false;
}
};
return {
loading,
error,
login,
getProductList
};
}
2.3 在 React 中使用
src/hooks/useApi.ts
import { useState } from 'react';
import apiManager from '@/utils/api';
import type { APIResponse } from '@/types/api';
export function useApi() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<any>(null);
const userModule = apiManager.getModule('user');
const productModule = apiManager.getModule('product');
const login = async (username: string, password: string) => {
setLoading(true);
setError(null);
try {
const res = await userModule.login({
body: { username, password }
});
return res.data;
} catch (err) {
setError(err);
throw err;
} finally {
setLoading(false);
}
};
const getProductList = async (page: number = 1, size: number = 10) => {
setLoading(true);
try {
const res = await productModule.getProductList({
queryParams: { page, size }
});
return res.data;
} catch (err) {
setError(err);
throw err;
} finally {
setLoading(false);
}
};
return {
loading,
error,
login,
getProductList
};
}