共计 2611 个字符,预计需要花费 7 分钟才能阅读完成。
传统方案的痛点分析
在大型前端项目中,我们通常使用 axios 作为 HTTP 客户端的基础库。但随着项目规模增长,传统封装方式逐渐暴露出一些问题:

- 重复样板代码 :每个 API 都需要手动处理错误、添加 loading 状态、拼接 URL 参数等
- 类型支持薄弱 :响应数据类型往往需要手动声明,难以与后端 API 保持同步
- 拦截器膨胀 :各种全局和局部的拦截逻辑混杂,难以维护
- 模块化不足 :API 分散在各处,缺乏统一的组织方式
这些痛点导致我们的请求层代码变得臃肿,类型安全也难以保障。
为什么选择 Trae
相比 axios,Trae 作为新一代 HTTP 客户端具有以下优势:
- 极简设计 :核心代码仅有 3KB,API 设计更加简洁
- TypeScript 原生支持 :从底层设计就考虑类型系统
- 可组合的中间件 :拦截器系统更灵活
- 现代化的 Promise API:取消请求等特性更易实现
基础封装实现
1. 创建基础请求实例
/**
* 创建基础 Trae 实例
* @param baseURL 基础 API 地址
* @returns 配置好的 Trae 实例
*/
import trae from 'trae';
import type {TraeInstance} from 'trae';
function createRequest(baseURL: string): TraeInstance {
const request = trae.create({
baseURL,
timeout: 15000,
});
// 请求拦截器
request.before((config) => {
// 添加认证 token
if (store.state.token) {config.headers.Authorization = `Bearer ${store.state.token}`;
}
return config;
});
return request;
}
export const request = createRequest(import.meta.env.VITE_API_BASE);
2. 增强类型支持
/**
* 封装 GET 请求
* @param url 请求地址
* @param params 查询参数
* @param config 额外配置
*/
export function get<T = any>(
url: string,
params?: Record<string, any>,
config?: TraeRequestConfig
): Promise<T> {return request.get<T>(url, { params, ...config}).then(res => res.data);
}
// 类似地封装 post/put/delete 等方法
模块化 API 组织
推荐按业务模块组织 API,目录结构如下:
src/
api/
modules/
user.ts # 用户相关 API
product.ts # 产品相关 API
index.ts # 统一导出
types.ts # 全局类型定义
示例模块实现:
// api/modules/user.ts
import {get, post} from '../request';
type UserInfo = {
id: number;
name: string;
avatar: string;
};
export const userApi = {
/** 获取用户信息 */
getInfo: (userId: number) => get<UserInfo>(`/user/${userId}`),
/** 更新用户信息 */
updateInfo: (data: Partial<UserInfo>) => post('/user/update', data),
};
统一错误处理
通过 after 拦截器实现全局错误处理:
request.after((response) => {
// 成功响应直接返回
if (response.status < 400) {return response;}
// 处理 401 未授权
if (response.status === 401) {router.push('/login');
return Promise.reject(new Error('请重新登录'));
}
// 其他错误
const error = new Error(response.data?.message || '请求失败');
return Promise.reject(error);
});
进阶优化方案
请求取消
利用 AbortController 实现请求取消:
const controller = new AbortController();
get('/api/data', {}, {signal: controller.signal});
// 需要取消时调用
controller.abort();
并发控制
对于高频请求场景,可以限制并发数:
import pLimit from 'p-limit';
// 限制最大并发数为 5
const limit = pLimit(5);
const fetchData = (id: number) =>
limit(() => get(`/data/${id}`));
// 同时发起多个请求会自动排队
const results = await Promise.all(ids.map(id => fetchData(id))
);
避坑指南
Trae 类型扩展
当需要扩展 Trae 的默认配置时,可以通过声明合并:
declare module 'trae' {
interface TraeRequestConfig {
// 添加自定义配置
silent?: boolean; // 是否静默处理错误
retry?: number; // 重试次数
}
}
与 Vue3 集成
在 setup 中使用时,建议配合 async/await:
import {userApi} from '@/api/modules/user';
const userInfo = ref<UserInfo>();
async function loadUser() {
try {userInfo.value = await userApi.getInfo(123);
} catch (err) {console.error('加载用户失败', err);
}
}
思考题
在微前端架构下,如何实现跨子应用的请求共享?可以考虑以下方向:
- 通过主应用提供统一的请求实例
- 使用自定义协议实现应用间通信
- 利用浏览器存储共享认证信息
希望这篇文章能帮助你更好地封装前端 HTTP 请求层。Trae 的轻量化和 TypeScript 友好特性,确实能显著提升大型项目的开发体验。如果有任何问题或建议,欢迎留言讨论。
正文完
发表至: 前端开发
近三天内
