react的3中请求

📘 前端 API 调用方式详解:`fetch` vs `callApi` vs `API.invoke`

> **适用读者**:React + TypeScript 开发者,Nx + Django REST Framework 项目使用者

> **目标**:理解三种主流前端 API 调用方式的原理、适用场景与权衡


## 📌 一、背景说明

在现代 Web 应用中,前端需要与后端 API 通信。根据项目架构不同,调用方式也不同。本文以 **Nx + React + Jotai + DRF (Django REST Framework)** 项目为例,对比三种调用方式:

  1. **`fetch`**:浏览器原生 API

  2. **`callApi`**:项目封装的通用调用函数(基于 RPC 风格)

  3. **`API.invoke`**:底层 RPC 方法调用(通常由 `callApi` 封装)


## 🧱 二、1. `fetch`:原生 HTTP 客户端

💡 是什么?

  • 浏览器内置的 **原生 HTTP 请求 API**

  • 无需任何依赖,所有现代浏览器都支持

🔧 示例代码

```ts

// 删除组织(直接调用 REST API)

const deleteOrganization = async (orgId: number) => {

const response = await fetch(`/api/organizations/${orgId}`, {

method: 'DELETE',

credentials: 'include', // 携带 Cookie(用于认证)

headers: {

'Content-Type': 'application/json',

...(csrfToken && { 'X-CSRFToken': csrfToken }),

}

});

if (!response.ok) {

throw new Error('删除失败');

}

return await response.json(); // 或 response.text()

};

```

✅ 优点

  • **零依赖**:不依赖任何库

  • **完全控制**:可自定义 headers、body、method 等

  • **简单直接**:适合一次性、低频操作(如删除、文件下载)

❌ 缺点

  • **重复代码多**:每次都要写认证、错误处理、CSRF

  • **无统一错误处理**:错误格式不一致

  • **无类型安全**:除非手动定义

  • **不支持方法名抽象**:URL 硬编码,难维护

🎯 适用场景

  • 项目初期快速验证

  • 一次性操作(如 `DELETE /api/xxx`)

  • 不在主业务流程中的边缘功能


## 🧱 三、2. `API.invoke`:RPC 风格底层调用

💡 是什么?

  • 项目自定义的 **RPC(远程过程调用)接口**

  • 前端调用 **方法名**(如 `"changePassword"`),后端映射到具体函数

  • 通常由 DRF 的 `@action` 自动生成

🔧 示例代码

```ts

// 调用后端注册的 "changePassword" 方法

const result = await API.invoke("changePassword", {

body: {

password: "old123",

new_password: "new456"

}

});

// result 结构(由后端约定)

// { $meta: { ok: true }, data: { detail: "成功" } }

```

🔧 后端对应(DRF)

```python

apps/users/views.py

class UserViewSet(viewsets.GenericViewSet):

@action(detail=False, methods=["patch"], url_path="change-password")

def change_password(self, request):

... 逻辑

return Response({"detail": "成功"})

```

> DRF 自动生成方法名 `changePassword`(驼峰)

✅ 优点

  • **语义清晰**:`changePassword` 比 `/api/users/change-password/` 更易读

  • **自动映射**:URL 由后端生成,前端无需关心

  • **类型安全**:配合 TypeScript 泛型

  • **统一响应结构**:如 `{ $meta, data }`

❌ 缺点

  • **耦合生成机制**:依赖 OpenAPI / DRF 自动生成

  • **调试困难**:方法名与 URL 不直观对应

  • **灵活性低**:不能自由指定 headers/method

🎯 适用场景

  • 项目已建立 **RPC 风格 API 体系**

  • 使用 **DRF ViewSet + @action** 架构

  • 需要强类型和统一响应格式


🧱 四、3. `callApi`:项目封装的通用调用层

💡 是什么?

  • 对 `API.invoke` 的 **进一步封装**

  • 通常提供:

  • 自动错误弹窗(toast)

  • 全局 loading 状态

  • 认证头自动注入

  • 错误格式标准化

🔧 示例代码

```ts

// 在 React 组件中使用

import { useApi } from "@/hooks/useApi";

const MyComponent = () => {

const { callApi } = useApi();

const handleChangePassword = async () => {

const response = await callApi("changePassword", {

body: { password: "...", new_password: "..." }

});

if (!response?.ok) {

// 错误会自动显示,无需手动处理

return;

}

// 成功

toast.success("修改成功");

};

};

```

html 复制代码
// 在 React 组件中使用
import { useApi } from "@/hooks/useApi";

const MyComponent = () => {
  const { callApi } = useApi();

  const handleChangePassword = async () => {
    const response = await callApi("changePassword", {
      body: { password: "...", new_password: "..." }
    });

    if (!response?.ok) {
      // 错误会自动显示,无需手动处理
      return;
    }

    // 成功
    toast.success("修改成功");
  };
};
```

🔧 内部实现(简化版)

html 复制代码
```ts
// hooks/useApi.ts
export const useApi = () => {
  const callApi = async (method: string, options: ApiCallOptions) => {
    try {
      const response = await API.invoke(method, options);
      
      if (!response.$meta.ok) {
        toast.error(response.data?.detail || "操作失败");
        return { ok: false, data: response.data };
      }

      return { ok: true, data: response.data };
    } catch (error) {
      toast.error("网络错误");
      return { ok: false, data: null };
    }
  };

  return { callApi };
};
```

✅ 优点

  • **开发效率高**:自动处理 loading/error/toast

  • **一致性好**:整个项目 API 调用风格统一

  • **解耦业务逻辑**:组件只关注"做什么",不关心"怎么做"

❌ 缺点

  • **黑盒感强**:新手难以理解内部逻辑

  • **定制性差**:特殊需求(如自定义 header)需额外配置

  • **调试需跳转多层**

🎯 适用场景

  • **中大型项目**,需统一 API 调用规范

  • 团队协作,避免重复造轮子

  • 已有成熟错误处理/认证体系


📊 五、对比总结

| 特性 | `fetch` | `API.invoke` | `callApi` |

|------|--------|--------------|----------|

| **依赖** | 无 | 项目 SDK | 项目 Hook |

| **控制粒度** | 高 | 中 | 低 |

| **错误处理** | 手动 | 手动 | 自动 |

| **认证/CSRF** | 手动 | 自动 | 自动 |

| **类型安全** | 无 | 有(需生成) | 有 |

| **可维护性** | 低(URL 硬编码) | 高(方法名抽象) | 高 |

| **适用阶段** | 原型 / 小项目 | 中大型项目 | 成熟项目 |


🛠 六、如何选择?

| 你的项目状态 | 推荐方式 |

|--------------|----------|

| 刚起步,快速验证 | `fetch` |

| 已用 DRF ViewSet + 自动生成 SDK | `API.invoke` |

| 有统一错误处理、Toast、Loading 体系 | `callApi` |

| 需要最大灵活性(如上传、WebSocket) | `fetch` |

> ✅ **最佳实践**:

> - **主业务流程** → 用 `callApi`

> - **边缘操作**(如删除、导出)→ 用 `fetch`

> - **避免混用**:同一功能不要既用 `fetch` 又用 `callApi`


🔚 七、结语

三种方式不是互斥的,而是 **分层协作**:

```

React Component

callApi() ← 开发者主要调用这里

API.invoke() ← 自动生成的方法调用

fetch() ← 底层 HTTP 请求

```

理解每一层的职责,才能写出**可维护、健壮、高效**的前端代码。


> 📝 **博客建议标题**:

> 《前端 API 调用三剑客:fetch、invoke 与 callApi 的实战对比》

> 《从 fetch 到 callApi:现代前端 API 调用演进之路》

欢迎根据你的项目细节补充示例和截图!如需 Markdown 源文件,我可直接提供。

相关推荐
仰望星空@脚踏实地15 小时前
DataKit js-yaml和follow-redirects组件依赖影响分析
前端·datakit
Mr_fang7194015 小时前
iframe 导致 Vue Router go(-1) 无法正常返回问题解决方案
前端
Drift_Dream15 小时前
Node.js 第二课:用核心模块构建你的第一个服务器
前端·后端
DEMO派16 小时前
首页图片懒加载实现方案解析
前端
用户9520816117916 小时前
百度地图MapVThree Editor:地图编辑
前端
程序员龙语16 小时前
CSS 文本样式与阴影属性
前端·css
LYFlied16 小时前
【每日算法】LeetCode 152. 乘积最大子数组(动态规划)
前端·算法·leetcode·动态规划
狼与自由16 小时前
excel 导入 科学计数法问题处理
java·前端·excel
小徐_233316 小时前
不如摸鱼去的 2025 年终总结,今年的关键词是直面天命
前端·年终总结
GISer_Jing16 小时前
交互式圣诞树粒子效果:手势控制+图片上传
前端·javascript