You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React.js 如何实现类似Angular的API分组管理最佳实践(Axios)

React + Axios 类Angular风格API目录规范实现

以下结构完全对齐Angular分层管理API的逻辑,和你之前使用的常量、服务分层写法一一对应,无额外学习成本。

目录结构

完全对齐Angular核心服务+特性模块服务的分层逻辑,所有API相关代码统一收敛管理:

src/
└── api/                     # API统一管理根目录
    ├── core/                # 全局请求核心配置(对应Angular全局HTTP拦截器、环境配置层)
    │   ├── constants.ts     # 全局常量:接口根地址、通用状态码等
    │   └── request.ts       # Axios实例封装、全局请求/响应拦截器
    ├── modules/             # 分业务模块的接口定义(对应Angular各特性模块的service目录)
    │   └── upload/          # 以上传模块为例,每个业务模块单独建文件夹
    │       ├── constants.ts # 当前模块的接口路径常量
    │       └── service.ts   # 当前模块的接口请求方法封装
    └── index.ts             # 所有服务统一导出入口

各文件代码实现(和你Angular写法一一对应)

1. 全局核心配置层

api/core/constants.ts

全局通用常量,对应Angular环境配置文件中定义的API根地址:

// Vite项目用import.meta.env,CRA/webpack项目替换为process.env.REACT_APP_xxx即可
export const BASE_API_URL = import.meta.env.VITE_API_BASE_URL;
export const SUCCESS_CODE = 200;
export const AUTH_FAIL_CODE = 401;

api/core/request.ts

Axios实例与拦截器封装,对应Angular中配置了拦截器的HttpClient单例:

import axios from 'axios';
import { BASE_API_URL, AUTH_FAIL_CODE } from './constants';

const request = axios.create({
  baseURL: BASE_API_URL,
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json'
  }
});

// 请求拦截器:统一加token、请求标识,和Angular请求拦截器逻辑完全一致
request.interceptors.request.use((config) => {
  const token = localStorage.getItem('access_token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 响应拦截器:统一处理错误、响应格式转换,和Angular响应拦截器逻辑完全一致
request.interceptors.response.use(
  (response) => response.data,
  (error) => {
    if (error.response?.status === AUTH_FAIL_CODE) {
      // 统一处理未登录逻辑:清缓存、跳登录页
      localStorage.removeItem('access_token');
      window.location.pathname = '/login';
    }
    return Promise.reject(error);
  }
);

export default request;

2. 业务模块层(以上传模块为例,完全匹配你写的Angular示例)

api/modules/upload/constants.ts

当前模块接口路径常量,和你Angular项目中写的constants.ts完全一致:

export const uploadData = `/uploadData`;

api/modules/upload/service.ts

当前模块请求方法封装,和你Angular项目中写的service.ts完全一致,类+单例的模式和Angular服务保持统一:

import request from '../../core/request';
import * as constants from './constants';

class UploadService {
  // 方法定义、参数和你Angular里的service方法完全对齐
  uploadData(formData: FormData): Promise<any> {
    return request.post(constants.uploadData, formData, {
      headers: { 'Content-Type': 'multipart/form-data' }
    });
  }
}

// 导出单例,和Angular服务的单例逻辑一致,无需重复实例化
export default new UploadService();

api/index.ts

服务统一导出入口,方便业务组件引入:

export { default as uploadService } from './modules/upload/service';
// 其他业务模块服务在此处追加导出即可,比如userService、orderService等

3. 组件中调用方式

和你Angular中引入服务调用的逻辑完全对齐,不需要在组件里写任何请求配置,直接调用服务方法即可:

import { uploadService } from '@/api';
import { useState } from 'react';

const UploadPage = () => {
  const [uploadRes, setUploadRes] = useState(null);

  const handleFileUpload = async (file: File) => {
    const formData = new FormData();
    formData.append('file', file);
    // 对应你Angular里pipe(take(1)).subscribe的逻辑,await拿到的res就是subscribe中的返回值
    try {
      const res = await uploadService.uploadData(formData);
      // 业务逻辑写在这里,和你之前// do something的位置完全对应
      setUploadRes(res);
    } catch (err) {
      console.error('上传失败', err);
    }
  };

  return (
    <div>
      {/* 上传组件UI */}
    </div>
  );
};

export default UploadPage;

落地规范要点

  • 所有接口路径必须定义在对应模块的constants.ts中,禁止在service方法里硬编码接口地址
  • 所有接口请求必须封装在对应模块的service类方法中,禁止在组件内直接写axios/fetch调用
  • 业务模块按领域拆分,比如用户相关接口放modules/user、订单相关接口放modules/order,和Angular特性模块的拆分逻辑一致
  • 全局通用逻辑(token注入、错误处理、loading控制)全部收敛在core/request.ts的拦截器中,业务模块不需要重复处理
  • 所有service均导出单例,和Angular服务的单例模式保持一致,避免重复创建实例

内容的提问来源于stack exchange,提问作者Elliot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 18:27:34