news 2026/5/3 15:58:40

Vue3 + Vite项目实战:手把手教你封装一个带Token自动管理的Axios请求库

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue3 + Vite项目实战:手把手教你封装一个带Token自动管理的Axios请求库

Vue3 + Vite项目实战:打造企业级Axios请求库的自动化设计

在当今前端工程化实践中,一个健壮的HTTP请求库早已不是简单的请求发送工具,而是承载着Token管理、错误处理、性能监控等多项职责的基础设施。本文将带您从工程化角度,重构一个真正具备生产级质量的请求库解决方案。

1. 请求库架构设计理念

现代前端项目的HTTP请求库需要解决三个核心问题:自动化可维护性一致性。我们先来看一个典型的请求生命周期中需要处理的环节:

graph TD A[发起请求] --> B[Token注入] B --> C[参数处理] C --> D[实际请求] D --> E[状态码处理] E --> F[数据格式化] F --> G[错误处理]

基于这个流程,我们的请求库需要实现以下关键特性:

  • 智能Token管理:自动注入、刷新和失效处理
  • 统一错误处理:网络异常、业务异常的分类处理
  • 请求生命周期钩子:提供各个阶段的扩展点
  • 类型安全:完整的TypeScript支持
  • 性能监控:请求耗时统计和异常上报

2. 核心实现方案

2.1 创建增强型Axios实例

不同于基础封装,我们需要创建一个具备扩展能力的Axios实例:

// src/libs/http-client.ts import axios from 'axios' import type { AxiosInstance, AxiosRequestConfig } from 'axios' class HttpClient { private instance: AxiosInstance private interceptors: HttpInterceptors constructor(config: HttpRequestConfig) { this.instance = axios.create(config) this.interceptors = config.interceptors || {} this.setupInterceptors() } private setupInterceptors() { // 请求拦截器链 this.instance.interceptors.request.use( this.interceptors.request?.success, this.interceptors.request?.failure ) // 响应拦截器链 this.instance.interceptors.response.use( this.interceptors.response?.success, this.interceptors.response?.failure ) } public request<T>(config: HttpRequestConfig): Promise<T> { return this.instance.request(config) } // 其他HTTP方法封装... } export default HttpClient

2.2 Token自动化管理

实现Token的自动注入和刷新机制需要考虑多种场景:

// Token管理策略 const tokenStrategy = { getToken(): string | null { // 从存储中获取 }, refreshToken(): Promise<string> { // 调用刷新接口 }, shouldRefresh(error): boolean { // 判断是否需要刷新 }, isExpired(token): boolean { // 检查Token过期 } } // 请求队列管理 const pendingRequests = new Map() // 拦截器配置 instance.interceptors.request.use(async (config) => { if (config.requireAuth !== false) { let token = tokenStrategy.getToken() if (token && tokenStrategy.isExpired(token)) { if (!pendingRequests.has('refreshing')) { pendingRequests.set('refreshing', true) token = await tokenStrategy.refreshToken() pendingRequests.delete('refreshing') } else { return new Promise((resolve) => { const unsubscribe = () => { pendingRequests.delete('waiting') resolve(instance(config)) } pendingRequests.set('waiting', unsubscribe) }) } } config.headers.Authorization = `Bearer ${token}` } return config })

2.3 智能错误处理系统

建立分级的错误处理机制:

错误类型处理方式用户提示
网络错误自动重试/跳转网络设置"网络连接不可用"
401未授权清除Token跳转登录"登录已过期"
403禁止访问提示权限不足"无访问权限"
500服务器错误上报异常/展示备用UI"服务暂时不可用"
业务逻辑错误透传错误码根据业务码显示对应提示
// 错误处理器 class ErrorHandler { static handle(error) { if (error.isAxiosError) { switch (error.response?.status) { case 401: this.handleUnauthorized() break case 403: this.handleForbidden() break // 其他状态码处理... } } // 业务错误处理 if (error.bizCode) { showToast(error.bizMessage) } // 异常上报 trackError(error) } }

3. 高级功能实现

3.1 请求取消与竞态处理

实现请求防抖和取消机制:

// 请求取消令牌管理 const cancelTokenMap = new Map() function createCancelToken(key: string) { if (cancelTokenMap.has(key)) { cancelTokenMap.get(key).cancel() } const source = axios.CancelToken.source() cancelTokenMap.set(key, source) return source.token } // 使用示例 async function fetchData(params) { try { const res = await request({ url: '/api/data', cancelToken: createCancelToken('data-request') }) // 处理数据 } catch (err) { if (!axios.isCancel(err)) { // 处理真实错误 } } }

3.2 性能监控集成

在拦截器中加入性能统计:

instance.interceptors.request.use((config) => { config.metadata = { startTime: Date.now() } return config }) instance.interceptors.response.use( (response) => { const duration = Date.now() - response.config.metadata.startTime trackApiPerformance({ url: response.config.url, duration, status: response.status }) return response }, (error) => { if (error.config) { const duration = Date.now() - error.config.metadata.startTime trackApiPerformance({ url: error.config.url, duration, status: error.response?.status || 0, isError: true }) } return Promise.reject(error) } )

4. 工程化实践建议

4.1 配置管理最佳实践

推荐的项目配置结构:

src/ libs/ http-client/ # 请求库核心 index.ts # 主入口 interceptors/ # 拦截器实现 types/ # 类型定义 utils/ # 工具函数 config/ http.ts # HTTP相关配置 env.ts # 环境变量处理

环境变量处理方案:

// config/env.ts export function getApiBaseUrl() { return import.meta.env.VITE_API_BASE ?? '/api' } export function getApiTimeout() { return Number(import.meta.env.VITE_API_TIMEOUT) || 10000 }

4.2 团队协作规范

制定团队内的HTTP请求规范:

  1. API定义规范

    • 所有API必须定义在src/api目录下
    • 按业务模块组织文件结构
    • 必须使用TypeScript接口定义请求/响应类型
  2. 错误处理规范

    • 业务错误必须包含错误码和用户友好提示
    • 网络错误要有统一的重试机制
    • 敏感错误不上报具体信息
  3. 性能优化建议

    • 列表接口默认启用分页
    • 大数据量响应启用压缩
    • 频繁调用的接口考虑本地缓存

5. 测试与调试技巧

5.1 Mock方案对比

方案优点缺点适用场景
Mock Service Worker浏览器层拦截,最接近真实配置复杂开发环境全量Mock
Vite Proxy Mock零配置,修改即时生效功能有限快速原型开发
本地JSON文件简单直接无法模拟动态场景静态数据模拟

推荐使用Mock Service Worker进行API模拟:

// src/mocks/handlers.ts import { rest } from 'msw' export const handlers = [ rest.get('/api/user', (req, res, ctx) => { return res( ctx.delay(150), ctx.json({ id: 'user-1', name: 'John Doe' }) ) }) ]

5.2 调试工具链配置

推荐在开发环境中添加以下调试支持:

// vite.config.js export default defineConfig({ plugins: [ { name: 'configure-response', configureServer(server) { server.middlewares.use((req, res, next) => { res.setHeader('Access-Control-Expose-Headers', 'X-Request-Id') next() }) } } ] })

在Chrome开发者工具中,可以通过自定义日志标签过滤请求:

// 在请求拦截器中 console.log( '%cAPI Request%c %s %o', 'background: #4CAF50; color: white; padding: 2px 4px; border-radius: 3px;', '', config.method?.toUpperCase(), { url: config.url, params: config.params, data: config.data } )

6. 性能优化策略

6.1 请求缓存实现

基于内存的缓存方案:

const cache = new Map() async function cachedRequest(config) { const cacheKey = JSON.stringify({ url: config.url, params: config.params }) if (cache.has(cacheKey)) { return cache.get(cacheKey) } const response = await instance.request(config) cache.set(cacheKey, response) // 设置过期时间 setTimeout(() => { cache.delete(cacheKey) }, config.cacheTTL || 30000) return response }

6.2 压缩与性能调优

推荐配置:

const instance = axios.create({ // 启用请求体压缩 decompress: true, // 优化大JSON响应 transformResponse: [ (data) => { try { return JSONbig.parse(data) } catch { return data } } ], // DNS缓存 httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }) })

7. 安全加固方案

7.1 常见安全防护

  • CSRF防护:自动注入XSRF-TOKEN
  • CSP合规:检查响应头安全策略
  • 敏感信息过滤:拦截器中的日志过滤
// 安全拦截器 const securityInterceptor = { onRequest(config) { // 过滤敏感字段 if (config.data?.password) { config.data.password = '[REDACTED]' } return config }, onResponse(response) { // 检查安全头 if (!response.headers['content-security-policy']) { reportSecurityIssue('Missing CSP header') } return response } }

7.2 敏感操作保护

关键操作需要二次确认:

function withConfirmation(requestFn, message) { return async function(...args) { await confirmDialog(message) return requestFn(...args) } } // 使用示例 const deleteUser = withConfirmation( (userId) => request.delete(`/users/${userId}`), '确认删除该用户?' )

8. 项目集成示例

8.1 Vue3组合式API集成

创建可复用的HTTP Hook:

// src/composables/useHttp.ts export function useHttp() { const loading = ref(false) const error = ref(null) const request = async (config) => { loading.value = true error.value = null try { const response = await httpClient.request(config) return response } catch (err) { error.value = err throw err } finally { loading.value = false } } return { loading, error, request } }

8.2 Pinia状态管理集成

将请求状态纳入状态管理:

// stores/api.ts export const useApiStore = defineStore('api', { state: () => ({ pendingRequests: 0, lastError: null }), actions: { async callApi(config) { this.pendingRequests++ try { const response = await httpClient.request(config) this.lastError = null return response } catch (error) { this.lastError = error throw error } finally { this.pendingRequests-- } } } })

9. 演进路线规划

9.1 架构演进方向

  1. 微前端适配

    • 支持多实例共存
    • 主子应用通信桥接
  2. Serverless集成

    • 云函数调用封装
    • 自动凭证管理
  3. GraphQL适配层

    • 查询批处理
    • 缓存策略集成

9.2 性能监控扩展

建议增加的监控维度:

  • 请求成功率统计
  • 慢请求分析
  • 依赖服务健康度检查
  • 用户端网络质量采样
// 监控插件示例 const monitoringPlugin = { install(httpClient) { httpClient.interceptors.response.use( (response) => { reportSuccess(response) return response }, (error) => { reportError(error) return Promise.reject(error) } ) } }

10. 疑难问题解决方案

10.1 常见问题排查

问题1:Token刷新竞态条件

解决方案:

let refreshPromise = null async function getToken() { if (tokenExpired()) { if (!refreshPromise) { refreshPromise = refreshToken().finally(() => { refreshPromise = null }) } return refreshPromise } return currentToken }

问题2:文件上传进度丢失

解决方案:

const uploadFile = (file, onProgress) => { const formData = new FormData() formData.append('file', file) return request.post('/upload', formData, { onUploadProgress: (progressEvent) => { const percent = Math.round( (progressEvent.loaded * 100) / progressEvent.total ) onProgress(percent) } }) }

10.2 调试技巧

推荐使用以下Chrome开发者工具技巧:

  1. 网络请求过滤

    • 使用domain:api.example.com过滤特定域名请求
    • 使用larger-than:1M查找大体积请求
  2. 性能分析

    • 使用Performance面板记录请求瀑布流
    • 检查Timing标签中的各个阶段耗时
  3. 断点调试

    // 在请求拦截器中添加debugger instance.interceptors.request.use((config) => { if (config.url.includes('/sensitive')) { debugger } return config })

11. 前沿技术展望

11.1 WebSocket集成方案

实现实时数据与HTTP请求的统一管理:

class HybridClient { private http: HttpClient private socket: WebSocket constructor() { this.http = new HttpClient() this.socket = new WebSocket('wss://api.example.com') this.setupSocket() } private setupSocket() { this.socket.onmessage = (event) => { this.emit('message', JSON.parse(event.data)) } } public request(config) { if (config.realtime) { return new Promise((resolve) => { const handler = (data) => { if (data.id === config.id) { this.off('message', handler) resolve(data) } } this.on('message', handler) this.socket.send(JSON.stringify(config)) }) } return this.http.request(config) } }

11.2 Server-Sent Events适配

处理服务器推送事件:

function createEventSource(url) { const eventSource = new EventSource(url) return { onMessage(handler) { eventSource.onmessage = (event) => { handler(JSON.parse(event.data)) } }, close() { eventSource.close() } } }

12. 移动端适配策略

12.1 弱网处理方案

实现离线优先策略:

const offlineCache = new Map() async function offlineRequest(config) { if (navigator.onLine) { try { const response = await request(config) offlineCache.set(config.cacheKey, response) return response } catch (err) { if (offlineCache.has(config.cacheKey)) { return offlineCache.get(config.cacheKey) } throw err } } else if (offlineCache.has(config.cacheKey)) { return offlineCache.get(config.cacheKey) } throw new Error('Offline and no cached data') }

12.2 请求优先级管理

基于业务重要性的优先级队列:

class PriorityQueue { private high: Array<() => Promise<any>> private normal: Array<() => Promise<any>> private low: Array<() => Promise<any>> add(task: () => Promise<any>, priority: 'high' | 'normal' | 'low') { this[priority].push(task) this.process() } private processing = false private async process() { if (this.processing) return this.processing = true while (this.high.length || this.normal.length || this.low.length) { const task = this.high.shift() || this.normal.shift() || this.low.shift() try { await task() } catch (err) { console.error('Queue task failed:', err) } } this.processing = false } }

13. 微前端场景适配

13.1 主子应用通信

通过自定义事件实现跨应用通信:

// 主应用 window.mainAppBridge = { request: (config) => httpClient.request(config) } // 子应用 function proxyRequest(config) { if (window.mainAppBridge) { return window.mainAppBridge.request(config) } return localRequest(config) }

13.2 共享实例方案

// 共享模块 let sharedInstance = null export function getSharedHttpClient() { if (!sharedInstance) { sharedInstance = new HttpClient({ baseURL: '/api', interceptors: { request: [authInterceptor], response: [errorInterceptor] } }) } return sharedInstance }

14. 测试策略设计

14.1 单元测试重点

测试关键拦截器逻辑:

describe('Auth Interceptor', () => { it('should add token to headers', async () => { localStorage.setItem('token', 'test-token') const instance = axios.create() instance.interceptors.request.use(authInterceptor) const config = await instance.interceptors.request.handlers[0].fulfilled({ headers: {} }) expect(config.headers.Authorization).toBe('Bearer test-token') }) })

14.2 E2E测试方案

使用Cypress进行API测试:

// cypress/e2e/api.cy.js describe('API Suite', () => { it('should handle auth failure', () => { cy.intercept('POST', '/auth', { statusCode: 401, body: { code: 'AUTH_FAILED' } }) cy.visit('/login') cy.get('#submit').click() cy.contains('.error', '认证失败').should('be.visible') }) })

15. 部署与运维

15.1 健康检查方案

实现API健康监控端点:

// 健康检查路由 router.get('/health', (req, res) => { const checks = { database: checkDatabase(), cache: checkCache(), thirdParty: checkThirdPartyAPI() } Promise.all(Object.values(checks)) .then(() => res.status(200).json({ status: 'healthy' })) .catch(() => res.status(500).json({ status: 'unhealthy' })) })

15.2 灰度发布支持

通过Header控制请求路由:

instance.interceptors.request.use((config) => { if (localStorage.getItem('beta-user') === 'true') { config.headers['X-API-Version'] = 'v2' } return config })

16. 性能优化进阶

16.1 请求合并策略

实现类似GraphQL的批处理:

class BatchRequest { private batch: Array<{ config: any, resolve: Function, reject: Function }> = [] private timer: any = null add(config) { return new Promise((resolve, reject) => { this.batch.push({ config, resolve, reject }) if (!this.timer) { this.timer = setTimeout(() => this.flush(), 50) } }) } private async flush() { const batch = this.batch this.batch = [] this.timer = null try { const response = await axios.post('/batch', { requests: batch.map(item => item.config) }) batch.forEach((item, index) => { item.resolve(response.data.results[index]) }) } catch (err) { batch.forEach(item => item.reject(err)) } } }

16.2 预加载方案

基于路由的API预加载:

router.beforeEach((to) => { if (to.meta.preloadApis) { to.meta.preloadApis.forEach(api => { api.load() }) } }) // 路由配置示例 { path: '/dashboard', component: Dashboard, meta: { preloadApis: [userApi, statsApi] } }

17. 安全防护进阶

17.1 请求签名方案

防止请求篡改:

function createSign(config) { const timestamp = Date.now() const nonce = Math.random().toString(36).slice(2) const str = [ config.method, config.url, timestamp, nonce, JSON.stringify(config.data) ].join('|') const sign = crypto .createHmac('sha256', SECRET_KEY) .update(str) .digest('hex') config.headers['X-Timestamp'] = timestamp config.headers['X-Nonce'] = nonce config.headers['X-Sign'] = sign return config }

17.2 敏感操作审计

记录关键操作日志:

instance.interceptors.request.use((config) => { if (config.sensitive) { auditLog({ action: config.url, params: config.data, user: currentUser }) } return config })

18. 监控体系集成

18.1 全链路追踪

实现请求链路追踪:

instance.interceptors.request.use((config) => { const traceId = generateTraceId() config.headers['X-Trace-Id'] = traceId startTrace(traceId, config) return config }) instance.interceptors.response.use( (response) => { endTrace(response.config.headers['X-Trace-Id'], 'success') return response }, (error) => { if (error.config) { endTrace(error.config.headers['X-Trace-Id'], 'failed') } return Promise.reject(error) } )

18.2 性能指标采集

采集关键性能指标:

const metrics = { requestCount: 0, successCount: 0, errorCount: 0, durationSum: 0 } instance.interceptors.response.use( (response) => { metrics.requestCount++ metrics.successCount++ metrics.durationSum += response.duration return response }, (error) => { metrics.requestCount++ metrics.errorCount++ return Promise.reject(error) } ) // 定期上报 setInterval(() => { reportMetrics({ ...metrics, avgDuration: metrics.durationSum / metrics.requestCount }) }, 60000)

19. 国际化支持

19.1 多语言错误处理

根据用户语言返回错误信息:

class I18nErrorHandler { static messages = { 'en-US': { 401: 'Unauthorized', 404: 'Not Found' }, 'zh-CN': { 401: '未授权', 404: '资源不存在' } } static handle(error, lang = 'en-US') { const message = this.messages[lang]?.[error.status] || error.message showToast(message) } }

19.2 区域化配置

不同区域的API配置:

function getRegionalConfig() { const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone const region = timezone.startsWith('Asia') ? 'APAC' : 'EMEA' return { baseURL: REGION_CONFIG[region].apiBase, timeout: REGION_CONFIG[region].timeout } }

20. 设计模式应用

20.1 策略模式应用

可插拔的拦截器策略:

interface InterceptorStrategy { onRequest?(config): any; onResponse?(response): any; onError?(error): any; } class InterceptorManager { private strategies: InterceptorStrategy[] = [] use(strategy: InterceptorStrategy) { this.strategies.push(strategy) } apply(instance) { instance.interceptors.request.use( (config) => this.strategies.reduce((c, s) => s.onRequest?.(c) ?? c, config) ) instance.interceptors.response.use( (response) => this.strategies.reduce((r, s) => s.onResponse?.(r) ?? r, response), (error) => this.strategies.reduceRight((e, s) => s.onError?.(e) ?? e, error) ) } }

20.2 装饰器模式应用

增强请求功能:

function withRetry(times = 3) { return function(target, key, descriptor) { const original = descriptor.value descriptor.value = async function(...args) { let lastError for (let i = 0; i < times; i++) { try { return await original.apply(this, args) } catch (error) { lastError = error await new Promise(resolve => setTimeout(resolve, 1000 * i)) } } throw lastError } return descriptor } } class ApiService { @withRetry(3) async fetchData() { return request.get('/data') } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/3 15:55:56

小学生如何零基础学习入门微积分

小学生零基础学习微积分&#xff0c;关键在于‌用直观、可视化的方式理解核心思想‌&#xff0c;而非死记公式或进行复杂计算。根据公开资料和教学实践&#xff0c;以下是适合小学生的入门路径&#xff1a; ‌一、理解微积分的两大核心思想‌ ‌1、微分&#xff08;Differenti…

作者头像 李华
网站建设 2026/5/3 15:55:52

基于MCP协议构建AI智能体共享记忆库:FixFlow实战指南

1. 项目概述&#xff1a;为AI智能体构建一个“集体记忆”如果你和我一样&#xff0c;每天都在和Claude、Cursor这类AI编程助手打交道&#xff0c;那你肯定也经历过这种抓狂时刻&#xff1a;你的AI助手昨天刚解决了一个棘手的Supabase RLS策略错误&#xff0c;今天遇到一模一样的…

作者头像 李华
网站建设 2026/5/3 15:52:16

ChatTTS对话式语音合成:从原理到实战部署指南

1. 项目概述&#xff1a;ChatTTS&#xff0c;一个为对话场景而生的语音合成模型如果你正在为你的AI助手、虚拟主播或者任何需要“开口说话”的交互式应用寻找一个自然、富有表现力的语音合成方案&#xff0c;那么ChatTTS绝对值得你花时间深入了解。它不是一个传统的、听起来像机…

作者头像 李华