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 源文件,我可直接提供。

相关推荐
渴望成为python大神的前端小菜鸟43 分钟前
VUE 面试题
前端·javascript·vue.js·面试题
想要成为糕糕手1 小时前
深入理解 JavaScript 中的 “this”:从自由变量到绑定规则
前端·javascript
北极象1 小时前
Electron + Playwright 一文多发应用架构设计
前端·javascript·electron
咖猫1 小时前
guacamole-web 1.5.5 index.html
前端·javascript·html
getapi1 小时前
Express 是一个基于 Node.js 的轻量级、灵活的 Web 应用框架,广泛用于构建后端服务和 API
前端·node.js·express
渣波1 小时前
🧳 我的 React Trip 之旅(5):我的 AI 聊天机器人,今天又把用户气笑了
前端·javascript
boombb1 小时前
数据驱动与CSS预定义样式:实现灵活多变的Banner布局
前端
JIngJaneIL1 小时前
基于Java失物招领系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·vue
鼎道开发者联盟1 小时前
当界面会思考:AIGUI八要素驱动DingOS实现“感知-生成-进化“闭环
前端·人工智能·ai·gui