别再写 API 路由了:Server Actions 才是全栈 React 的终极形态

前言:一种名为"胶水代码"的疲惫

接上回。咱们用 Next.js App RouterServer Components 实现了服务端直接读数据库。页面加载速度快得离谱,Google 爬虫也爱死我们了。

但是,只要涉及到操作(比如提交表单、点赞、收藏),你的痛苦回忆又回来了。

按照老规矩(Next.js Pages Router 时代),你要做一个"添加待办事项"的功能,你得这么折腾:

  1. pages/api/todos.ts 里写一个 API Handler,解析 req.body,连数据库写入,返个 JSON。
  2. 在前端组件里,引入 axios
  3. 写个 handleSubmit,调用 axios.post('/api/todos', data)
  4. 处理 loading 状态,处理 error。
  5. 请求成功后,为了让列表更新,还得手动去更新本地 state 或者触发 SWR/React Query 的 mutate

累不累? 我不就为了存一行字吗?为什么要跨越千山万水,写这么多"胶水代码"来连接前后端?

今天,我们要把这层胶水撕掉。欢迎来到 Server Actions 的世界。在这里,前端按钮可以直接调用后端函数


核心魔法:远程过程调用 (RPC) 的文艺复兴

什么是 Server Action? 简单说,就是你在服务器文件里写一个函数,标上 'use server',然后你可以直接把这个函数 import 到前端组件里,绑在 onClick 上。

Next.js 在背后会自动帮你把这个函数调用变成一个 HTTP POST 请求。你看着像是在调用本地函数,其实是在搞 RPC(远程过程调用)

❌ 以前的写法(前后端分离,心也分离):

1. 后端 (pages/api/createTodo.ts):

ts 复制代码
export default async function handler(req, res) {
  if (req.method === 'POST') {
    const todo = await db.todo.create({ data: req.body });
    res.status(200).json(todo);
  }
}

2. 前端 (components/AddTodo.tsx):

const 复制代码
  const [text, setText] = useState('');
  const add = async () => {
    await axios.post('/api/createTodo', { text }); // 还要记 URL
    // 手动刷新列表...
  };
  return Add;
}

✅ Server Actions 写法(合二为一):

我们新建一个 actions.ts 文件,这里面的代码只会在服务器运行

  1. 定义 Action (actions.ts):
'use 复制代码
  1. 使用 Action (components/AddTodo.tsx):
import 复制代码
const AddTodo = () => {
  // 直接把 Server Action 绑在 form 的 action 上
  // 甚至关了 JS 这表单都能提交(渐进增强)
  return (
    
      
      Add
    
  );
}

发现了吗?axios 不见了,useEffect 不见了,手动刷新数据的逻辑也不见了。 你写代码的感觉就像回到了 PHP 时代(褒义),直接跟数据库对话,但享受着 React 的组件化体验。

进阶玩法:如果不只是 Form 提交怎么办?

"将军,我不是提交表单,我就想点个赞,或者点击按钮执行个逻辑,怎么办?"

这时候我们不能用 `` 了,我们需要在 Event Handler 里调用。 但是,直接调用 Server Action 是拿不到 loading 状态的。

这时候,React 的 useTransition 也就是为此而生的。

'use 复制代码

这体验简直绝了。 你不需要自己定义 const [loading, setLoading] = useState(false)useTransition 帮你搞定了一切 loading 态管理。

避坑指南:别太天真,这还是 HTTP

虽然写起来像本地函数,但你心里要有数:这依然是一次网络请求

1. 安全,安全,还是TMD安全!

千万别以为这是一个普通函数,就觉得能在里面随便传东西。 Server Action 本质上就是一个公开的 API 接口。 任何人都可以通过抓包,模拟发送 POST 请求给这个 Action。

所以,必须在 Action 内部做身份验证和权限校验!

// 复制代码
export async function deletePost(id: string) {
  // 必须检查用户是否有权限!
  const session = await getSession();
  if (!session || session.user.role !== 'admin') {
    throw new Error('你没有权限干这事儿');
  }

  // 必须校验输入参数!(Zod 再次登场)
  const safeId = z.string().parse(id);

  await db.post.delete({ where: { id: safeId } });
}

2. 不能传递复杂对象

因为 Action 调用要跨越网络(序列化),所以参数和返回值必须是可序列化 的。 你不能把一个 onClick 回调函数或者一个 Class 实例传给 Server Action。 传 JSON、String、FormData 都是最稳的。

3. 不要把敏感数据传回客户端

Server Action 的返回值会发给浏览器。 别手滑把 user.password_hash 或者整个数据库连接对象给 return 出来了。


总结:全栈的最后一公里

Server Actions 的出现,标志着 React 正式从一个 UI 库,进化成了一个全栈框架。

它打破了"前端"和"后端"之间那道厚厚的墙。

  • 对于简单的 CRUD,你再也不用去写繁琐的 REST API。
  • 你的业务逻辑高度内聚:数据库操作代码和触发它的按钮代码,物理距离只有几行 import。

当然,这并不意味着后端工程师失业了。对于复杂的微服务编排、高并发处理,还是需要专业的后端架构。 但对于我们这种做应用、做产品的开发者来说,Server Actions 让我们能用原来 1/3 的代码量,干完 100% 的活

这就叫生产力。


下期预告 :代码写完了,测试跑通了。最后一步是什么?部署! 你还在手动 SSH 连服务器、装 Nginx、传文件吗?太 Low 了。 下一篇(完结篇),我们来聊聊 "CI/CD 与 Vercel 部署" 。教你如何把 GitHub 代码提交的一瞬间,自动触发构建、部署,并让你的应用跑在全世界的边缘节点(Edge)上。

相关推荐
王小酱2 小时前
Cursor 的 Debug模式的核心理念和使用流程
前端·cursor
前端老宋Running2 小时前
跟“白屏”说拜拜:用 Next.js 把 React 搬到服务器上,Google 爬虫都要喊一声“真香”
前端·react.js·架构
玉宇夕落2 小时前
深入理解 React 与 JSX:从组件到 UI 构建
前端·react.js
jun_不见2 小时前
面试官:你能说下订阅发布模式么,怎么在VUE项目中实现一个类似eventBus的事件总线呢
前端·javascript·面试
How_doyou_do2 小时前
前端动画的多种实现方式
前端
南山安2 小时前
React学习:组件化思想
javascript·react.js·前端框架
xhxxx2 小时前
别再被 CSS 定位搞晕了!5 种 position 一图打尽 + 实战代码全公开
前端·css·html
OpenTiny社区2 小时前
从千问灵光 App 看生成式 UI 技术的发展
前端·vue.js·ai编程
学术大白2 小时前
Flask 三层架构驱动的高效 AI Agent Web平台搭建
架构