跳到主要内容

Next.js Server Actions

Server Actions 是 App Router 里非常关键的一块能力。它让“写操作”不再一定要先绕到单独接口层,再从客户端手工拼一次请求。

先把它理解成什么

可以把 Server Actions 理解成:

  • 定义在服务端的可调用函数
  • 常用于表单提交和数据写入
  • 可以直接接上重定向、重验证和错误处理

它不是要彻底取代 Route Handlers,而是给页面级写操作提供更顺手的一条主线。

一个最小例子

'use server';

import { revalidatePath } from 'next/cache';

export async function createPost(formData: FormData) {
const title = String(formData.get('title'));

await db.post.create({ title });
revalidatePath('/posts');
}

然后在组件里直接使用:

import { createPost } from './actions';

export default function CreatePostForm() {
return (
<form action={createPost}>
<input name="title" />
<button type="submit">Create</button>
</form>
);
}

它为什么会比传统接口更顺

传统写法通常是:

  1. 前端提交表单
  2. 请求 /api/*
  3. 接口写数据库
  4. 前端再手工刷新列表或跳转

Server Actions 可以把这条链压得更短:

  1. 表单直接调用服务端函数
  2. 服务端写入
  3. 服务端立即 revalidatePath / revalidateTag
  4. 页面结果自动更新或再导航

什么时候适合用

很适合:

  • 表单提交
  • 后台管理写操作
  • 设置页更新
  • 点赞、收藏、评论、增删改这类页面内动作

不一定适合:

  • 复杂公共 API
  • 给第三方系统调用的接口
  • 需要独立开放给外部客户端的后端能力

这类场景通常还是 Route Handlers 或独立服务更稳。

参数和返回值怎么理解

Server Action 常见参数包括:

  • FormData
  • 普通序列化参数

例如:

'use server';

export async function updateStatus(id: string, status: 'draft' | 'published') {
await db.post.update(id, { status });
}

在客户端组件里:

'use client';

import { updateStatus } from './actions';

export function PublishButton({ id }: { id: string }) {
return (
<button onClick={() => updateStatus(id, 'published')}>
Publish
</button>
);
}

常和哪些能力一起出现

revalidatePath

写完数据后,刷新某个页面路径。

revalidateTag

写完数据后,按数据标签失效。

redirect

写完数据后直接跳转。

'use server';

import { redirect } from 'next/navigation';

export async function createInvoice(formData: FormData) {
await db.invoice.create({
title: String(formData.get('title')),
});

redirect('/invoices');
}

useActionState

适合把提交结果和错误信息回传给 UI。

错误处理怎么做

常见思路有两种:

1. 返回结构化结果

'use server';

export async function createUser(_: unknown, formData: FormData) {
const email = String(formData.get('email'));

if (!email) {
return { error: 'email is required' };
}

await db.user.create({ email });
return { success: true };
}

2. 抛异常,交给错误边界处理

适合更全局的失败情况。

安全边界要注意什么

1. Server Action 在服务端执行,不代表自动安全

鉴权、授权、输入校验仍然都要做。

2. 不要把只有客户端才能决定的业务假设直接信任

例如用户角色、价格、可编辑字段等,都应该在服务端再次确认。

3. 写操作后一定要想清楚缓存失效

否则最常见的结果就是:写入成功了,但页面还是旧数据。

Route Handlers 和 Server Actions 怎么选

更适合 Server Actions

  • 页面内写操作
  • 表单驱动流程
  • 只服务当前站点 UI

更适合 Route Handlers

  • 标准 REST / JSON API
  • Webhook
  • 给移动端、小程序、第三方客户端调用
  • 需要独立协议边界的服务端接口

常见误区

1. 以为有了 Server Actions 就再也不需要接口

不是。两者解决的问题不完全一样。

2. 忘记做重验证

这是最容易踩的坑。数据写进去了,但读缓存没失效,页面看起来像没更新。

3. 把大量复杂服务端逻辑都堆进 action 文件

Action 更适合做流程入口。重逻辑最好继续沉到 lib/ 或 service 层。

推荐继续往下看

  1. 缓存与重验证
  2. 升级检查清单

参考资料