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

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

接上回。咱们用 Next.js App Router 和 Server 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)上。

相关推荐
陈随易6 小时前
bm2,Node.js 与 Bun 项目部署新选择
前端·后端·程序员
福兮说7 小时前
canvas 旋转图片的六个坑:四角被裁、Math.ceil 多出 1px 黑边、JPG 角发黑、翻转方向反了
前端·javascript·图像处理·canvas
传人once9 小时前
多图片上传预览功能如何写
前端·css·html·jquery·html5
Eric.469 小时前
Wan‑Animate+MiniMax-H3 本地部署 AI 漫剧流水线:8G 显存动作迁移与动态分镜工程实战
前端·人工智能·comfyui·ai漫剧
Dawson Zhu10 小时前
《Agentic Design Patterns》第 1 章导读:提示词链(Prompt Chaining)
人工智能·语言模型·架构·aigc·agi
feiyu_gao11 小时前
Mindcraft:从个人实践到可复用模式集
架构·aigc·ai编程
库拉镜像AI牛牛11 小时前
短剧内容自动化生产:知漫剧工作室落地教程
大数据·服务器·前端·人工智能·语音识别
abigalexy11 小时前
Claude Code从零搭建新项目全流程AI实现-宠物生命周期管理App
架构·aigc·ai编程
律宏阔11 小时前
Flutter 列表图片内存优化:CachedNetworkImage 与 ClipRRect 的取舍
前端·flutter
行者全栈架构师11 小时前
【鸿蒙心迹】相册文搜图工程化落地:HarmonyOS 7.0 端侧 AI 检索全链路实战与 5 个踩坑解法
前端