基于Trae的HTTP客户端封装实践:提升前端请求效率与可维护性

10次阅读
没有评论

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

image.webp

传统方案的痛点分析

在大型前端项目中,我们通常使用 axios 作为 HTTP 客户端的基础库。但随着项目规模增长,传统封装方式逐渐暴露出一些问题:

基于 Trae 的 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);
  }
}

思考题

在微前端架构下,如何实现跨子应用的请求共享?可以考虑以下方向:

  1. 通过主应用提供统一的请求实例
  2. 使用自定义协议实现应用间通信
  3. 利用浏览器存储共享认证信息

希望这篇文章能帮助你更好地封装前端 HTTP 请求层。Trae 的轻量化和 TypeScript 友好特性,确实能显著提升大型项目的开发体验。如果有任何问题或建议,欢迎留言讨论。

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