1. 为什么需要工程化的请求层在中大型前端项目中接口请求就像城市的交通网络。如果每个司机开发者都随意选择路线请求方式很快就会导致交通瘫痪代码混乱。我曾经参与过一个电商项目初期没有统一封装请求层结果发现18种不同的错误处理方式5种token校验逻辑散落在各个角落生产环境出现跨域问题却无法快速定位TypeScriptAxios的组合就像给城市装上了智能交通系统。通过工程化封装我们可以实现类型安全像交通信号灯一样规范数据流动统一管控所有请求经过标准化收费站可观测性实时监控道路状况请求状态2. 基础封装从玩具到工具2.1 安装与基础配置先准备好施工材料npm install axios npm install --save-dev types/axios基础封装就像搭建毛坯房// src/libs/http.ts import axios, { AxiosInstance } from axios class Http { private instance: AxiosInstance constructor() { this.instance axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 }) } public getT(url: string) { return this.instance.getT(url) } }这个阶段常见三个坑没有处理环境变量导致多环境适配困难timeout设置不合理移动端建议15-30秒忘记配置withCredentials导致cookie丢失2.2 拦截器请求的安检系统完整的拦截器应该像机场安检一样分层// 请求拦截 this.instance.interceptors.request.use(config { const token getAuthToken() if (token) { config.headers.Authorization Bearer ${token} } // 灰度发布标识 if (isGrayUser()) { config.headers[X-Gray-Release] true } return config }) // 响应拦截 this.instance.interceptors.response.use( response { // 业务状态码处理 if (response.data.code ! 200) { return Promise.reject(response.data.message) } return response.data }, error { // 网络层错误转换 if (error.message.includes(timeout)) { return Promise.reject(请求超时请检查网络) } return Promise.reject(error) } )3. 进阶工程化实践3.1 类型系统的深度集成好的类型系统应该像精密齿轮组// 定义业务响应结构 interface ApiResponseT unknown { code: number data: T message?: string traceId?: string } // 带分页的数据结构 interface PaginatedDataT { items: T[] total: number } // 在封装方法中应用 public async getWithPaginationT( url: string, params: PaginationParams ): PromisePaginatedDataT { const response await this.instance.getApiResponsePaginatedDataT(url, { params }) return response.data.data }类型提示能帮我们避免字段拼写错误数据类型不匹配分页参数遗漏3.2 多环境动态配置工程化配置应该像变形金刚// config/env.ts export enum EnvMode { DEV development, TEST test, PROD production } const envMap { [EnvMode.DEV]: { baseURL: https://dev.api.com, traceEnabled: true }, [EnvMode.TEST]: { baseURL: https://test.api.com, traceEnabled: true }, [EnvMode.PROD]: { baseURL: https://api.com, traceEnabled: false } } // 在Http类中动态获取配置 constructor() { const env import.meta.env.MODE as EnvMode this.instance axios.create(envMap[env]) }4. 企业级功能扩展4.1 请求监控体系完整的监控应该包含// 在拦截器中添加监控点 request.interceptors.response.use(response { // 记录成功请求 monitor.trackApiSuccess({ url: response.config.url, duration: Date.now() - response.config.metadata.startTime, status: response.status }) return response }, error { // 记录失败请求 monitor.trackApiError({ url: error.config.url, errorCode: error.response?.status, errorMsg: error.message }) return Promise.reject(error) }) // 在请求配置中添加元数据 config.metadata { startTime: Date.now(), retryCount: 0 }4.2 智能重试机制像优秀快递员一样的重试策略private async requestWithRetryT(config: AxiosRequestConfig): PromiseT { const MAX_RETRY 3 let retryCount 0 const attempt async (): PromiseT { try { const response await this.instance.requestT(config) return response.data } catch (error) { if (shouldRetry(error) retryCount MAX_RETRY) { retryCount await sleep(1000 * retryCount) // 指数退避 return attempt() } throw error } } return attempt() } // 判断哪些错误需要重试 function shouldRetry(error: AxiosError) { return ( !error.response || error.response.status 408 || error.response.status 500 ) }5. 团队协作规范5.1 接口定义管理推荐使用SwaggerTypeScript的协作模式通过swagger-typescript-api生成类型定义将生成的类型文件放入src/types/api目录建立领域模型映射// src/types/api/user.ts export interface UserDTO { id: number name: string avatar_url: string } // 转换为前端模型 export class UserModel { constructor(public id: number, public name: string, public avatar: string) {} static fromDTO(dto: UserDTO): UserModel { return new UserModel(dto.id, dto.name, dto.avatar_url) } }5.2 代码审查清单在团队中实施请求层CR时检查是否使用了统一的http实例错误处理是否规范类型定义是否完整敏感参数是否加密是否添加了必要的监控点6. 性能优化技巧6.1 请求缓存策略像浏览器缓存一样智能const cache new Mapstring, { expire: number; data: any }() public async getWithCacheT( url: string, config?: AxiosRequestConfig { cacheTTL?: number } ): PromiseT { const cacheKey JSON.stringify({ url, params: config?.params }) if (cache.has(cacheKey)) { const item cache.get(cacheKey)! if (item.expire Date.now()) { return item.data } } const data await this.getT(url, config) cache.set(cacheKey, { data, expire: Date.now() (config?.cacheTTL || 60000) }) return data }6.2 并发控制避免洪水般的请求class RequestQueue { private activeCount 0 private queue: (() Promisevoid)[] [] private MAX_CONCURRENT 5 async add(requestFn: () Promiseany) { return new Promise((resolve, reject) { const task async () { try { this.activeCount const result await requestFn() resolve(result) } catch (error) { reject(error) } finally { this.activeCount-- this.next() } } if (this.activeCount this.MAX_CONCURRENT) { task() } else { this.queue.push(task) } }) } private next() { if (this.queue.length 0 this.activeCount this.MAX_CONCURRENT) { const task this.queue.shift()! task() } } }在实际项目中落地这套方案时建议采用渐进式策略。我通常会先实现核心的拦截器和类型系统等团队适应后再逐步添加监控、缓存等高级功能。记住好的架构不是一次性设计出来的而是在不断解决实际问题中演化出来的。