从零封装一个高可用的trae请求库:核心实现与避坑指南

7次阅读
没有评论

共计 1870 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

背景痛点:为什么需要二次封装

在大型前端项目中直接使用 axios 会暴露以下问题:

从零封装一个高可用的 trae 请求库:核心实现与避坑指南

  • 每个请求都要重复配置 baseURL、timeout 等参数
  • 错误处理分散在各处,无法统一监控 401/500 等状态码
  • 缺乏类型约束,响应数据结构后期难以维护
  • 拦截器管理混乱,多人协作时容易相互覆盖

技术选型:trae vs 原生 axios 封装

trae 作为 axios 的轻量级包装器,主要优势在于:

  • 内置 TypeScript 类型支持
  • 拦截器采用洋葱模型(可参考 Koa 中间件机制)
  • 更简洁的 API 设计(如 trae.create() 替代 axios.create)

原生封装的优势是灵活性更高,但需要自行处理类型扩展和拦截器堆叠问题。

核心实现

1. 基础请求封装

// 定义响应体泛型
interface ResponseData<T> {
  code: number
  data: T
  message?: string
}

// 创建带类型的请求实例
const request = trae.create({
  baseUrl: 'https://api.example.com',
  headers: {'X-Requested-With': 'trae'}
})

// 泛型请求方法
function get<T>(url: string, params?: object) {
  return request
    .get<ResponseData<T>>(url, { params})
    .then(res => res.data)
    .catch(handleError)
}

2. 拦截器分层设计

flowchart LR
    A[请求拦截] --> B[认证注入]
    B --> C[日志记录]
    D[响应拦截] --> E[错误预处理]
    E --> F[数据格式化]
// 认证拦截器
request.before(config => {config.headers.Authorization = getToken()
  return config
})

// 错误拦截器
request.after(response => {if (response.status >= 400) {throw new Error(response.data?.message || '请求失败')
  }
  return response
}, error => {if (error.response?.status === 401) {router.push('/login')
  }
  return Promise.reject(error)
})

3. 取消请求实现

const controllers = new Map()

function cancelableRequest(url: string) {const controller = new AbortController()
  controllers.set(url, controller)

  return request.get(url, {signal: controller.signal}).finally(() => {controllers.delete(url)
  })
}

// 组件卸载时调用
function cleanup() {controllers.forEach(ctrl => ctrl.abort())
  controllers.clear()}

生产环境优化

内存泄漏防范

  • 在 React 的 useEffect 清理函数中调用 cancelableRequest
  • Vue 的 beforeDestroy 钩子中执行清理

错误重试策略

function retryRequest(
  fn: Function, 
  retries = 3,
  delay = 1000
) {return fn().catch(err => {
    return retries > 1
      ? new Promise(resolve => 
          setTimeout(() => resolve(retryRequest(fn, retries - 1, delay * 2)), 
            delay
          )
        )
      : Promise.reject(err)
  })
}

常见问题

  1. 拦截器顺序问题
  2. trae 的拦截器按注册顺序执行
  3. 建议:错误处理拦截器最后注册

  4. Content-Type 陷阱

  5. POST 请求默认是 application/json
  6. 上传文件时需要明确设置:
    headers: {'Content-Type': 'multipart/form-data'}

扩展思考

现有的封装方案可以进一步适配:

  • GraphQL:封装 @apollo/client 的查询方法
  • WebSocket:结合 socket.io-client 建立双工通信
  • 请求 Mock:通过环境变量切换 MSW 拦截

完整的实现代码已放在 GitHub 仓库(伪代码示例需替换为实际项目链接),欢迎交流优化建议。

正文完
 0
评论(没有评论)