共计 1870 个字符,预计需要花费 5 分钟才能阅读完成。
背景痛点:为什么需要二次封装
在大型前端项目中直接使用 axios 会暴露以下问题:

- 每个请求都要重复配置 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)
})
}
常见问题
- 拦截器顺序问题
- trae 的拦截器按注册顺序执行
-
建议:错误处理拦截器最后注册
-
Content-Type 陷阱
- POST 请求默认是 application/json
- 上传文件时需要明确设置:
headers: {'Content-Type': 'multipart/form-data'}
扩展思考
现有的封装方案可以进一步适配:
- GraphQL:封装 @apollo/client 的查询方法
- WebSocket:结合 socket.io-client 建立双工通信
- 请求 Mock:通过环境变量切换 MSW 拦截
完整的实现代码已放在 GitHub 仓库(伪代码示例需替换为实际项目链接),欢迎交流优化建议。
正文完
发表至: 前端开发
近三天内
