人人都会AI编程

25.3 SSR 性能优化与缓存策略

更新时间:2026-07-10

Server Actions 是 React 19 中正式引入的一种新模式,它允许你在服务端定义异步函数,然后在客户端组件中像调用本地函数一样直接调用它们。React 会在幕后自动处理网络请求、参数序列化、响应处理以及界面更新,让你不用手动搭建 API 路由和编写 fetch 逻辑。

简单地说:Server Actions 让你把“客户端触发事件”和“服务端执行逻辑”无缝连接起来,就像同一个代码上下文里完成的一样。

为什么需要 Server Actions

在传统的前后端分离架构中,客户端想要修改服务端数据,通常需要:

  1. 编写一个服务端 API 路由(如 /api/update-profile)。
  2. 在客户端通过 fetch 或 Axios 发送请求,手动序列化参数。
  3. 处理响应、错误以及加载状态。
  4. 手动刷新页面数据或乐观更新 UI。

这个过程会引入大量样板代码,而且客户端和服务端之间的数据格式、校验逻辑、错误处理容易散落各处。Server Actions 将这些步骤抽象掉:你只需要写一个普通的异步函数(放在服务端执行),然后在客户端表单的 action 属性或按钮的 onClick 里引用它,剩下的都由 React 协同框架(如 Next.js)完成。

核心机制与底层原理

Server Actions 基于 RSC(React Server Components) 的通信能力。一个典型的 Server Action 具有以下特征:

  • 使用 'use server' 指令标记该函数只能在服务端运行。
  • 函数参数会被自动序列化(按照 React 的服务端-客户端协议)并发送到服务端。
  • 服务端执行完成后,可以选择返回一个新的 UI React Node(用于乐观更新或错误回显),也可以只返回一个可序列化的结果(如 JSON 对象、错误信息)。
  • 在并发渲染模式下,Server Action 可以自动结合 useTransitionuseOptimistic 等 Hooks 提供加载状态和乐观反馈。

整个过程对开发者透明,你不需要关心 fetch、请求头、响应状态码——这极大降低了数据变更类交互的开发成本。

实战示例:表单提交

下面是一个使用 Next.js App Router 实现“修改用户名”的完整例子,展示了 Server Actions 的全部关键步骤。

1. 定义 Server Action

在服务端组件文件中(例如 app/profile/page.tsx)或一个独立的 actions.ts 里,编写一个异步函数,加上 'use server'

// actions.ts
'use server';

import { db } from '@/lib/db';
import { revalidatePath } from 'next/cache';
import { z } from 'zod';

const schema = z.object({
  name: z.string().min(1, '姓名不能为空'),
});

export async function updateName(formData: FormData) {
  const parsed = schema.parse({
    name: formData.get('name'),
  });
  
  // 假设从 cookie 或 session 中拿到当前用户 ID
  const userId = await getCurrentUserId();
  await db.user.update({
    where: { id: userId },
    data: { name: parsed.name },
  });

  // 让缓存失效,页面重新获取最新数据
  revalidatePath('/profile');
  // 也可以直接返回一条消息用于客户端展示
  return { success: true };
}

2. 在客户端组件中调用

在需要提交的表单里,可以直接将 Server Action 作为 <form>action 属性值,无论是客户端组件还是服务端组件都可以:

// ProfileForm.tsx  (客户端组件)
'use client';

import { useFormStatus } from 'react-dom';
import { updateName } from './actions';

function SubmitButton() {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? '保存中...' : '保存'}
    </button>
  );
}

export default function ProfileForm() {
  return (
    <form action={updateName}>
      <input name="name" placeholder="新姓名" required />
      <SubmitButton />
    </form>
  );
}

这个表单在提交时:

  • 浏览器会将 <input> 的当前值收集为 FormData,并自动传递给 updateName
  • updateName 执行期间,useFormStatus 会返回 pending: true,按钮自动禁用并显示“保存中...”。
  • 执行完毕后,由于 revalidatePath 调用了,页面对应的数据会自动刷新,显示新用户名。

3. 处理错误与返回结果

Server Action 可以通过抛出异常来返回错误,也可以用更灵活的方式返回普通对象。React 增强的 useActionStateuseFormState(在 19 中标准化)可以帮助你在 UI 上展示服务端返回的消息:

'use client';
import { useActionState } from 'react';
import { updateName } from './actions';

export default function ProfileForm() {
  const [state, formAction] = useActionState(updateName, { success: false });

  return (
    <form action={formAction}>
      <input name="name" />
      {state?.error && <p className="text-red-500">{state.error}</p>}
      {state?.success && <p className="text-green-500">更新成功</p>}
      <SubmitButton />
    </form>
  );
}

并在 actions.ts 中修改 updateName 的返回结构,为两种状态提供消息:

export async function updateName(prevState: any, formData: FormData) {
  try {
    // ... 更新逻辑
    return { success: true };
  } catch (error) {
    return { error: '更新失败,请重试' };
  }
}

非表单场景的调用

Server Action 不仅局限于表单,你可以在任何客户端交互(如按钮点击、拖拽完成)中调用它。React 提供了 startTransition 包裹 Server Action 调用,从而让 UI 保持响应,并显示 pending 状态:

import { startTransition } from 'react';
import { updateProfile } from './actions';

function Button() {
  const handleClick = () => {
    startTransition(async () => {
      const result = await updateProfile({ bio: '新个人简介' });
      if (result.success) {
        // 展示成功 toast
      }
    });
  };

  return <button onClick={handleClick}>更新简介</button>;
}

注意:这种直接调用方式目前在一些框架中尚处于实验阶段,具体 API 需以框架文档为准,但核心思想相同。

Server Actions 与传统 API 路由的对比

| 特性 | Server Actions | 传统 API 路由 |
|------|----------------|----------------|
| 样板代码 | 极少,函数定义即接口 | 需要编写路由、请求处理、序列化 |
| 类型安全 | 天然端到端类型提示 | 需要手动维护请求/响应类型 |
| 加载状态 | 结合 useFormStatus / useTransition 自动获得 | 需要手动管理 loading、error 状态 |
| 错误处理 | 可以通过返回对象或抛出异常 | 需要自定义错误响应并解析 |
| 用途 | 数据变更(增删改)为主 | 适用于查询、外部回调、Webhook 等 |
| 面向的组件 | 主要配合服务端组件和表单 | 任何客户端操作 |

Server Actions 并不是要完全消灭 API 路由,而是把与组件直接相关的数据修改操作下沉到组件侧,减少开发者的上下文切换,让代码更内聚。

安全性与使用约束

Server Actions 背后是服务端运行的代码,因此必须注意安全:

  • 鉴权:与 API 路由一样,必须在函数体内验证用户身份,不能假设调用者就是合法用户。
  • 输入校验:永远不要信任客户端传过来的参数,应使用 Zod 等库进行严格校验。
  • 闭包限制'use server' 函数不能从外部作用域获取客户端相关的闭包变量(如 useState 里的值),只能接收序列化参数。
  • 体积与性能:Server Action 的执行是在服务端,每次调用都会产生一次网络往返,不适用于高频操作(如实时输入建议)。

总结

Server Actions 是 React 对通用数据变更模式的一次极具创造力的封装,它把原本需要两端各写一段代码的割裂体验,缩减到了一个函数定义、一行引用的程度。有了 Server Actions,表单提交、用户操作等工作变得异常简练,同时保持了类型安全与服务端逻辑的私密性。掌握 Server Actions 是现代 React 全栈开发的重要一环,配合 RSC、useOptimistic 等特性,可以构建出用户体验极佳且代码量极少的应用程序。