当前位置: 首页 > news >正文

别再复制粘贴了!手把手教你用TypeScript封装一个企业级axios请求库(附完整源码)

从零构建高可维护的TypeScript请求库:axios企业级封装实战

每次在Vue3项目中新建一个页面,你是否习惯性打开旧项目复制粘贴网络请求代码?当接口字段变更时,是否需要在十几个文件中逐个修改错误处理逻辑?这种重复劳动不仅降低开发效率,更会成为项目维护的噩梦。本文将带你从工程化角度,用TypeScript打造一个开箱即用、易于扩展的axios请求库,彻底告别碎片化的请求代码。

1. 为什么需要封装axios?

前端项目中,网络请求就像空气一样无处不在。但直接使用原生axios会导致以下问题:

  • 代码重复:每个请求都需要重复编写错误处理、loading状态管理
  • 类型缺失:响应数据没有类型约束,后期维护如同盲人摸象
  • 难以维护:拦截器逻辑分散在各处,修改时需全局搜索替换
  • 扩展困难:新需求如缓存、重试等特性无法统一实现

我们的封装目标是通过面向对象设计,实现以下特性:

// 理想中的调用方式 const user = await http.get<User>('/api/user', { cache: true // 自动缓存控制 })

2. 基础架构设计

2.1 类型系统先行

优秀的TS封装始于严谨的类型定义。我们先规划核心类型:

// 基础响应结构 interface ApiResponse<T = unknown> { code: number data: T message?: string } // 扩展的请求配置 interface RequestConfig extends AxiosRequestConfig { showLoading?: boolean retryCount?: number } // 错误处理类型 class RequestError extends Error { code?: number isNetworkError?: boolean }

2.2 类结构设计

采用工厂模式创建请求实例,便于后续多实例管理:

class RequestClient { private instance: AxiosInstance private interceptors: RequestInterceptors constructor(config: RequestConfig) { this.instance = axios.create(config) this.setupInterceptors() } private setupInterceptors() { // 拦截器实现 } public get<T>(url: string, config?: RequestConfig): Promise<T> { // 方法实现 } // 其他HTTP方法... }

3. 拦截器深度优化

3.1 请求拦截器实战

现代前端应用通常需要处理以下请求逻辑:

this.instance.interceptors.request.use(config => { // 1. 认证处理 const token = auth.getToken() if (token) { config.headers.Authorization = `Bearer ${token}` } // 2. 全局loading控制 if (config.showLoading !== false) { store.dispatch('showLoading') } // 3. 特殊header处理 if (config.url?.includes('/upload')) { config.headers['Content-Type'] = 'multipart/form-data' } return config })

3.2 响应拦截器最佳实践

响应处理需要兼顾业务约定和异常情况:

this.instance.interceptors.response.use( response => { // 1. 关闭loading store.dispatch('hideLoading') // 2. 处理二进制响应 if (response.config.responseType === 'blob') { return response.data } // 3. 业务状态码处理 const { code, data, message } = response.data if (code === 200) return data return Promise.reject( new RequestError(message || '业务错误', { code }) ) }, error => { // 统一错误处理 const requestError = new RequestError() if (error.response) { // HTTP状态码错误 requestError.code = error.response.status requestError.message = this.mapErrorCode(error.response.status) } else if (error.request) { // 网络错误 requestError.isNetworkError = true requestError.message = '网络连接异常' } else { // 其他错误 requestError.message = error.message } return Promise.reject(requestError) } )

4. 高级功能扩展

4.1 请求重试机制

对于不稳定的网络环境,自动重试能显著提升用户体验:

private async requestWithRetry<T>( method: HttpMethod, url: string, config?: RequestConfig ): Promise<T> { let retryCount = config?.retryCount || 0 while (retryCount >= 0) { try { return await this.request(method, url, config) } catch (error) { if (!this.shouldRetry(error) || retryCount <= 0) { throw error } retryCount-- await new Promise(resolve => setTimeout(resolve, this.getRetryDelay(retryCount)) ) } } throw new RequestError('请求失败') }

4.2 缓存控制实现

合理利用缓存可以减轻服务器压力:

private cache = new Map<string, { expire: number data: any }>() public async get<T>( url: string, config?: RequestConfig & { cacheTTL?: number } ): Promise<T> { const cacheKey = this.generateCacheKey(url, config) if (config?.cacheTTL) { const cached = this.cache.get(cacheKey) if (cached && cached.expire > Date.now()) { return cached.data } } const data = await this.request('get', url, config) if (config?.cacheTTL) { this.cache.set(cacheKey, { data, expire: Date.now() + config.cacheTTL * 1000 }) } return data }

5. 项目集成实践

5.1 Vue3组合式API封装

在组合式函数中提供更优雅的调用方式:

// src/composables/useHttp.ts export default function useHttp() { const http = new RequestClient({ baseURL: import.meta.env.VITE_API_URL }) const get = async <T>(url: string, config?: RequestConfig) => { try { return await http.get<T>(url, config) } catch (error) { // 统一UI错误处理 showToast(error.message) throw error } } return { get } }

5.2 环境变量管理

通过环境变量支持多环境配置:

# .env.development VITE_API_URL=https://dev.api.example.com VITE_API_TIMEOUT=5000 # .env.production VITE_API_URL=https://api.example.com VITE_API_TIMEOUT=10000
const http = new RequestClient({ baseURL: import.meta.env.VITE_API_URL, timeout: Number(import.meta.env.VITE_API_TIMEOUT) })

6. 测试与调试技巧

6.1 单元测试策略

使用jest编写健壮的测试用例:

describe('RequestClient', () => { let http: RequestClient beforeEach(() => { http = new RequestClient({ baseURL: 'https://api.example.com' }) jest.clearAllMocks() }) test('should add auth header when token exists', async () => { localStorage.setItem('token', 'test-token') const spy = jest.spyOn(axios, 'create') new RequestClient({}) expect(spy.mock.calls[0][0].headers.Authorization) .toBe('Bearer test-token') }) })

6.2 调试拦截器

在Chrome开发者工具中,可以通过以下方式调试:

  1. 在拦截器代码处打上断点
  2. 使用axios的请求/响应日志功能
  3. 通过performance标记观察请求时间线
// 在请求拦截器中添加性能标记 window.performance.mark('request_start')

7. 性能优化与安全

7.1 取消重复请求

防止快速点击导致的重复提交:

private pendingRequests = new Map<string, AbortController>() private addPendingRequest(config: RequestConfig) { const key = this.generateRequestKey(config) if (this.pendingRequests.has(key)) { this.pendingRequests.get(key)?.abort() } const controller = new AbortController() config.signal = controller.signal this.pendingRequests.set(key, controller) }

7.2 CSRF防护

自动处理CSRF token:

// 在请求拦截器中 const csrfToken = getCookie('csrfToken') if (csrfToken && ['post', 'put', 'delete'].includes(config.method!)) { config.headers['X-CSRF-Token'] = csrfToken }

8. 项目目录结构建议

规范的目录结构有助于长期维护:

src/ libs/ http/ index.ts # 主入口 types.ts # 类型定义 interceptors/ # 拦截器实现 auth.ts error.ts loading.ts plugins/ # 扩展功能 cache.ts retry.ts utils/ # 工具函数 helper.ts test/ http/ http.test.ts

9. 未来扩展方向

当基础请求库稳定后,可以考虑:

  • 集成GraphQL客户端支持
  • 添加WebSocket连接管理
  • 实现请求优先级队列
  • 开发可视化请求监控面板
interface AdvancedConfig { priority?: 'low' | 'normal' | 'high' analytics?: boolean }

在大型项目中,我们进一步将请求库与状态管理结合,实现了请求自动缓存到Vuex的功能。当组件卸载时自动取消pending请求,显著降低了内存泄漏风险。经过三个版本的迭代,现在这个请求库已经支撑了公司所有前端项目,平均为每个新项目节省了40%的网络请求相关代码量。

http://www.jsqmd.com/news/625317/

相关文章:

  • 教育IT负责人紧急必读:2026奇点大会锁定的4类技术债务+2个不可逆淘汰节点,6月30日前必须响应
  • 2026年4月目前可拆卸板式换热器公司,润滑油泵/风冷却系统/风冷式油冷却器/溢流阀,可拆卸板式换热器实力厂家有哪些 - 品牌推荐师
  • AI原生软件的“心脏手术”:如何在不中断线上服务前提下,完成特征管道热替换与模型灰度切流(附eBPF级可观测性注入方案)
  • C# 面试高频题:装箱和拆箱是如何影响性能的?负
  • OpenCV实战:图像拼接技术全景解析与优化策略
  • CodeMagicianT纺
  • EasyPlayer.js快速集成指南:从安装到实战应用
  • 成本-质量-时延三角平衡法则,深度拆解大模型MLOps评估中被90%团队忽略的3个隐性指标
  • 使用Spring AI Alibaba构建智能体Agent净
  • Agent-Sandbox UI 上线,来看看有哪些的功能是你经常使用的?悸
  • ENVI实战:基于Landsat 8影像的镶嵌与裁剪全流程解析
  • 别再只调学习率了!深入解读目标检测边框回归:从IoU到Shape-IoU的演进与选择指南
  • CTFshow平台PWN题逆向分析:从签到题看栈溢出漏洞防御
  • 5.1《深入浅出Linux 设备驱动框架》
  • 告别脚本与配置:DataX Web图形化界面重塑大数据同步工作流
  • 手把手教你用Claude2(这个AI挺能聊的)
  • 《剑指Offer》经典题目解析
  • Harness Engineering:Agent上下文压缩算法
  • 【UVM源码解析】uvm_queue:从SystemVerilog队列到UVM类的封装与演进
  • Visualized BGE批量推理实战:如何用Python代码将图片编码速度提升3倍
  • 告别“人眼找茬”:用STAR数据集+Python,5分钟让AI看懂卫星图里的“故事”
  • Hagicode.Libs:统一集成多个 AI 编程助手 CLI 的工程实践漳
  • 【Prompt工程黄金48小时】:为什么93%的工程师在奇点大会前两周才开始准备?附倒计时实战Checklist
  • 实战指南:Android12系统开机默认MTP模式配置与UsbDeviceManager深度解析
  • numpy报错终极排查手册:从multiarray导入失败看Python依赖管理的那些坑
  • 如何用开源智能工具一键提升你的英雄联盟游戏体验
  • 痞子衡嵌入式:turbo-spiboot - 一种基于MCUBoot协议的二级SPI加载APP提速方案壕
  • 如何在Windows电脑上使用Switch Joy-Con控制器玩游戏?
  • 别再死记硬背TCP三次握手了!用Wireshark抓包实战,带你搞懂连接建立的每个细节
  • 2025届毕业生推荐的六大降重复率方案实际效果