
Server Action 适合由 Next.js 页面发起的服务端变更,尤其是表单提交、权限校验和更新后刷新缓存;客户端 fetch 适合即时搜索、轮询、上传进度和需要显式控制请求生命周期的交互;公开给多个客户端使用的能力仍应设计 Route Handler 或独立后端 API。
目录
三种调用方式
| 方式 | 适合场景 | 主要边界 |
|---|---|---|
| Server Action | Next.js 内部表单和数据变更 | 不是通用公开 API |
| Client fetch → Route Handler | 高交互客户端请求 | 需要维护 HTTP 接口 |
| Client fetch → Strapi 等后端 | 后端本身是公开 API | 浏览器可见地址,受 CORS 和公开权限约束 |
Server Action 表单
// app/actions.ts
"use server"
import { revalidateTag } from "next/cache"
export async function createArticle(formData: FormData) {
const title = String(formData.get("title") ?? "").trim()
if (title.length < 3) {
return { ok: false, message: "标题至少 3 个字符" }
}
const user = await requireUser()
await saveArticle({ title, authorId: user.id })
revalidateTag("articles")
return { ok: true }
}
import { createArticle } from "@/app/actions"
export function ArticleForm() {
return (
<form action={createArticle}>
<input name="title" required minLength={3} />
<button type="submit">创建文章</button>
</form>
)
}
表单 Action 可以与 React 的 pending、结果状态能力配合,并保留渐进增强路径。但所有输入仍必须在服务端验证。
客户端 fetch
即时搜索不一定适合 Server Action:
"use client"
import { useEffect, useState } from "react"
export function SearchBox({ query }: { query: string }) {
const [results, setResults] = useState([])
useEffect(() => {
const controller = new AbortController()
fetch(`/api/search?q=${encodeURIComponent(query)}`, {
signal: controller.signal,
})
.then((response) => {
if (!response.ok) throw new Error(`Search failed: ${response.status}`)
return response.json()
})
.then(setResults)
.catch((error) => {
if (error.name !== "AbortError") console.error(error)
})
return () => controller.abort()
}, [query])
return <ResultList results={results} />
}
客户端 fetch 可以取消、并发和细粒度处理响应,但要自行处理 loading、错误、竞态与重试。
安全差异
Server Action 在服务器执行,不代表自动安全:
- 每次调用都验证当前用户身份和资源权限。
- 不信任隐藏 input、URL 参数和客户端传来的 ID。
- 对金额、角色和资源归属在服务端重新计算。
- 添加必要的速率限制和审计日志。
- 不把异常堆栈和后端响应原样返回用户。
客户端直接请求 Strapi 时,只能使用允许暴露的 Public 权限或用户自己的凭据。私密 Strapi API Token 必须留在 Server Action、Route Handler 或服务端数据层。
缓存刷新
Server Action 的优势之一是变更后可以调用 Next.js 的 revalidation API,让相关页面或 tag 失效。客户端 fetch 也能请求一个完成相同操作的 Route Handler,但需要自己设计协议。
不要在数据库写入失败时提前刷新缓存;也不要只刷新前端状态却忘记服务端缓存,导致刷新页面又看到旧数据。
何时不要使用 Server Action
- API 需要被移动端、第三方或其他服务调用。
- 需要标准 REST/Webhook 合约和独立版本管理。
- 上传需要精确进度、分片或直接传对象存储。
- 高频实时请求、轮询或搜索建议使用专门接口。
- 业务后端独立部署,Next.js 只是其中一个客户端。
选择流程
- 这是 Next.js 页面里的数据变更吗?优先评估 Server Action。
- 需要客户端持续控制请求、取消或显示上传进度吗?使用 fetch。
- 多种客户端都要调用吗?设计 Route Handler 或独立 API。
- 涉及私密凭据吗?调用必须经过服务端。
- 更新后是否影响缓存页面?明确 revalidation 策略。
如果后端是 Strapi,可以继续阅读 Next.js 连接 Strapi 5。组件边界不清楚时参考 Server 与 Client Components。
参考资料
继续阅读
React Hydration Failed:原因、定位与修复
系统排查服务器 HTML 与客户端首次渲染不一致的问题,覆盖时间、随机数、浏览器 API、无效 HTML、客户端存储、第三方扩展和 suppressHydrationWarning 的边界。
11 分钟Next.js 路由指南:App Router 常见模式
基于当前 Next.js App Router,梳理静态路由、动态路由、catch-all、route groups、parallel routes 和 route handlers 的使用场景,并说明什么时候还会遇到 Pages Router。
12 分钟Next.js 连接 Strapi 5:数据获取、缓存与安全
在 Next.js App Router 中封装 Strapi 5 REST 请求,处理 populate、缓存、错误、草稿预览和 API Token,避免把私密凭据暴露到客户端。
订阅 FreeMac
每周精选:免费 Mac 软件评测、可信来源更新、替代方案和少折腾指南。