从0死磕全栈之Next.js Server Actions 入门实战:在服务端安全执行逻辑,告别 API 路由!

在 Next.js 13.4+ 中,官方正式推出 Server Actions(服务端函数)功能,让你无需编写 API 路由,就能直接在服务端处理表单提交、数据库操作等逻辑。本文通过一个简单的"留言提交"例子,带你快速掌握 Server Actions 的用法、优势和最佳实践。


一、什么是 Server Actions?

Server Actions 是 Next.js 提供的一种新机制,允许你在服务端组件中定义函数 ,并通过客户端(如表单)直接调用它,而无需创建单独的 /api 路由

核心特点:

  • ✅ 函数在服务端执行(安全!)
  • ✅ 自动处理 CSRF 保护
  • ✅ 支持 async/await
  • ✅ 与 React 表单天然集成
  • ✅ 自动序列化参数(支持对象、数组等)

💡 它不是替代 API 路由,而是为简单、私有、页面专属的逻辑提供更简洁的方案。


server action的概念其实来自于React的server function,指的是​​运行在服务器端的函数​​,通常用于处理业务逻辑、访问数据库、执行权限校验,Server Function 是一个通用术语,泛指任何运行在服务端的函数。

server action是 Server Function 的一种​​特定用法​​:​​专门用于处理表单提交、按钮点击等用户交互触发的异步操作​​.一个函数如果被标记为 ​​action​​(比如通过 action={myAction}传递),或者从一个 action函数内部调用,那么它就被称为 ​​Server Action​​。

二、为什么用 Server Actions? vs 传统 API 路由

场景 传统 API 路由 Server Actions
表单提交 需写 /api/submit + fetch 直接调用函数,无需 fetch
数据验证 在 API 文件中处理 在函数内直接处理
错误反馈 需解析 JSON 响应 直接抛出错误,自动捕获
安全性 需手动防 CSRF 自动内置保护
代码量 多文件(API + 页面) 单文件搞定

🎯 适用场景:用户注册、评论提交、点赞、设置偏好等页面级操作


三、实战例子:实现一个"留言提交"功能

我们将创建一个页面,用户输入名字和留言,点击提交后保存到内存(模拟数据库),并刷新列表。

⚠️ 注意:Server Actions 仅在 App Router 中可用


第一步:启用 Server Actions(Next.js 13.4+ 默认开启)

确保 next.config.js 中启用了(新版默认已开):

js 复制代码
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: true, // Next.js 14+ 可省略
  },
};
export default nextConfig;

第二步:创建留言页面

创建文件:app/guestbook/page.js

jsx 复制代码
// app/guestbook/page.js
'use client';

import { useState, useActionState } from 'react';
import { submitMessage } from './actions';

export default function Guestbook() {
  const [name, setName] = useState('');
  const [message, setMessage] = useState('');
  const [state, formAction] = useActionState(submitMessage, { success: false, error: null });

  const handleSubmit = (e) => {
    e.preventDefault();
    formAction({ name, message });
    // 清空表单(可选)
    setName('');
    setMessage('');
  };

  return (
    <div style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
      <h1>📝 留言板</h1>

      {/* 显示结果 */}
      {state?.success && <p style={{ color: 'green' }}>✅ 留言成功!</p>}
      {state?.error && <p style={{ color: 'red' }}>{state.error}</p>}

      {/* 表单 */}
      <form onSubmit={handleSubmit}>
        <div>
          <input
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="你的名字"
            required
            style={{ display: 'block', margin: '0.5rem 0', padding: '0.5rem' }}
          />
        </div>
        <div>
          <textarea
            value={message}
            onChange={(e) => setMessage(e.target.value)}
            placeholder="留言内容"
            required
            rows="3"
            style={{ display: 'block', margin: '0.5rem 0', padding: '0.5rem' }}
          />
        </div>
        <button type="submit" style={{ padding: '0.5rem 1rem', backgroundColor: '#0070f3', color: 'white' }}>
          提交留言
        </button>
      </form>

      {/* 模拟显示留言(实际应从数据库读取) */}
      <div style={{ marginTop: '2rem' }}>
        <h2>已有留言</h2>
        <ul>
          <li>张三:Next.js 太棒了!</li>
          <li>李四:Server Actions 真香!</li>
        </ul>
      </div>
    </div>
  );
}

第三步:创建 Server Action(服务端函数)

创建文件:app/guestbook/actions.js

jsx 复制代码
// app/guestbook/actions.js
'use server';

// 模拟数据库(真实项目用 Prisma、Drizzle 等)
let messages = [];

export async function submitMessage(prevState, formData) {
  try {
    const { name, message } = formData;

    // 简单验证
    if (!name || !message) {
      return { success: false, error: '名字和留言不能为空!' };
    }

    // 模拟保存到数据库
    messages.push({ name, message, timestamp: new Date() });

    console.log('新留言:', { name, message });

    // 返回成功状态
    return { success: true, error: null };
  } catch (error) {
    return { success: false, error: '提交失败,请重试。' };
  }
}

🔑 关键点:

  • 文件顶部必须加 'use server'
  • 函数必须是 async
  • 第一个参数是前一次的状态(用于 useActionState),第二个是传入的数据。

第四步:运行并测试

bash 复制代码
npm run dev

访问 http://localhost:3000/guestbook,填写表单并提交。

你会看到:

  • 控制台打印日志(证明在服务端执行)
  • 页面显示"✅ 留言成功!"
  • 没有网络请求到 /api!数据直接通过 Server Action 处理

四、Server Actions 最佳实践

1. 只用于私有逻辑

  • 不要暴露敏感操作(如删除用户)给公开页面;
  • 适合当前用户专属操作(如设置偏好、提交评论)。

2. 配合身份验证

在函数内检查用户登录状态:

js 复制代码
'use server';
import { auth } from '@/auth';

export async function postComment(formData) {
  const session = await auth();
  if (!session) throw new Error('未登录');
  // ...保存评论
}

3. 错误处理要友好

  • 返回结构化状态(如 { success, error });
  • 避免直接 throw,除非你想中断流程。

4. 不要在客户端组件中定义

Server Action 必须定义在服务端上下文 中(即带 'use server' 的文件),不能写在 'use client' 组件里。

5. 参数自动序列化

支持传对象、数组、Date 等,但不能传函数、Symbol、undefined


五、常见问题 FAQ

Q1:Server Actions 能替代所有 API 路由吗?

不能。适合页面专属、简单操作;复杂、公共、需 CORS 的接口仍用 API 路由。

六、结语

Server Actions 是 Next.js 向"全栈一体化"迈出的重要一步。它让开发者用更少的代码、更安全的方式处理服务端逻辑,尤其适合表单交互场景。

记住:能用 Server Actions 解决的,就别写 API 路由了!

相关推荐
Ticnix9 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人9 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl9 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人10 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼10 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端
布列瑟农的星空10 小时前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust
Mr Xu_10 小时前
Vue 3 中计算属性的最佳实践:提升可读性、可维护性与性能
前端·javascript
jerrywus10 小时前
我写了个 Claude Code Skill,再也不用手动切图传 COS 了
前端·agent·claude
玖月晴空10 小时前
探索关于Spec 和Skills 的一些实战运用-Kiro篇
前端·aigc·代码规范