Next.js 16 入门教程
数据变更
本教程共 42 篇 · 第 10 篇 · 更新于 2026-07-30 · 约 8 分钟阅读
Next.jsNext.js 16 入门教程Server Actions数据变更表单revalidate
10. 数据变更
本节目标:学会用 Server Actions 处理数据变更,掌握表单提交、缓存清除和跳转的完整流程。
什么是 Server Actions
Server Actions 是在服务器端运行的异步函数,可以从客户端调用。它们用来处理数据变更——创建、更新、删除操作。
// app/actions.ts
'use server'
export async function createPost(formData: FormData) {
const title = formData.get('title')
const content = formData.get('content')
await db.post.create({
data: { title, content }
})
}
WarningServer Actions 可以通过 POST 请求直接调用。务必在函数内部验证用户身份和权限。
创建 Server Actions
独立文件
在文件顶部加 'use server',所有导出函数都是 Server Actions:
// app/lib/actions.ts
'use server'
import { auth } from '@/lib/auth'
export async function createPost(formData: FormData) {
const session = await auth()
if (!session?.user) {
throw new Error('未登录')
}
const title = formData.get('title')
const content = formData.get('content')
await db.post.create({
data: { title, content, authorId: session.user.id }
})
}
内联在 Server Component
export default function Page() {
async function createPost(formData: FormData) {
'use server'
// ...
}
return <form action={createPost}>...</form>
}
调用 Server Actions
表单调用
Server Component 中直接用 action prop:
import { createPost } from '@/app/actions'
export function Form() {
return (
<form action={createPost}>
<input type="text" name="title" />
<input type="text" name="content" />
<button type="submit">创建</button>
</form>
)
}
Client Component 中通过 import 调用:
'use client'
import { createPost } from '@/app/actions'
export function Form() {
return (
<form action={createPost}>
<input type="text" name="title" />
<button type="submit">创建</button>
</form>
)
}
事件处理中调用
'use client'
import { incrementLike } from './actions'
import { useState } from 'react'
export default function LikeButton({ initialLikes }: { initialLikes: number }) {
const [likes, setLikes] = useState(initialLikes)
return (
<>
<p>总赞数:{likes}</p>
<button
onClick={async () => {
const updatedLikes = await incrementLike()
setLikes(updatedLikes)
}}
>
点赞
</button>
</>
)
}
用 useActionState 处理状态
'use client'
import { useActionState } from 'react'
import { createPost } from '@/app/actions'
export function Form() {
const [state, formAction, pending] = useActionState(createPost, null)
return (
<form action={formAction}>
<input type="text" name="title" />
<button disabled={pending}>
{pending ? '提交中...' : '创建'}
</button>
</form>
)
}
useActionState 返回三个值:
state:Server Action 的返回值formAction:传给<form action={...}>的函数pending:是否正在执行
表单验证
Server Action 里可以验证表单数据:
'use server'
export async function createPost(prevState: any, formData: FormData) {
const title = formData.get('title') as string
const content = formData.get('content') as string
if (!title || title.length < 3) {
return { message: '标题至少 3 个字符' }
}
if (!content) {
return { message: '内容不能为空' }
}
await db.post.create({ data: { title, content } })
return { message: '创建成功' }
}
重新验证缓存
数据变更后,通常要清除缓存,让 UI 显示最新数据。
revalidatePath
按路径清除缓存:
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
// ... 创建文章
revalidatePath('/posts')
}
revalidateTag
按标签清除缓存:
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost(formData: FormData) {
// ... 创建文章
revalidateTag('posts', 'max')
}
NoteNext.js 16 中,
revalidateTag需要传入第二个参数指定缓存生命周期配置(如'max')。单参数写法已废弃。
updateTag
立即过期缓存(用户立刻看到变更):
'use server'
import { updateTag } from 'next/cache'
export async function createPost(formData: FormData) {
// ... 创建文章
updateTag('posts')
}
| 方法 | 使用场景 | 行为 |
|---|---|---|
revalidatePath | 不知道标签时 | 清除路径下所有缓存 |
revalidateTag | 允许短暂延迟 | 先返回旧数据,后台刷新 |
updateTag | 用户要立刻看到变更 | 立即过期,下次请求重新获取 |
跳转
数据变更后跳转到其他页面:
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
const post = await db.post.create({
data: { /* ... */ }
})
revalidatePath('/posts')
redirect(`/posts/${post.id}`)
}
Note
redirect会抛出异常,之后的代码不会执行。如果需要先清除缓存,在redirect之前调用。
操作 Cookie
Server Actions 可以读写 Cookie:
'use server'
import { cookies } from 'next/headers'
export async function setTheme(theme: string) {
const cookieStore = await cookies()
cookieStore.set('theme', theme)
}
设置或删除 Cookie 后,Next.js 会重新渲染当前页面和布局,UI 立即反映新值。
useEffect 中调用
有些场景需要在组件加载时触发变更:
'use client'
import { incrementViews } from './actions'
import { useState, useEffect, useTransition } from 'react'
export default function ViewCount({ initialViews }: { initialViews: number }) {
const [views, setViews] = useState(initialViews)
const [isPending, startTransition] = useTransition()
useEffect(() => {
startTransition(async () => {
const updatedViews = await incrementViews()
setViews(updatedViews)
})
}, [])
return <p>总浏览:{views}</p>
}
安全注意事项
- 验证身份:每个 Server Action 都要检查用户是否登录
- 验证权限:检查用户是否有权操作目标资源
- 验证输入:不要信任客户端传来的数据
- 限制频率:防止滥用(可用第三方库)
'use server'
export async function deletePost(formData: FormData) {
const session = await auth()
if (!session?.user) {
throw new Error('未登录')
}
const id = formData.get('id') as string
const post = await db.post.findUnique({ where: { id } })
if (post?.authorId !== session.user.id) {
throw new Error('无权删除')
}
await db.post.delete({ where: { id } })
revalidatePath('/posts')
}
小结
这一章我们学了数据变更的完整方案:
'use server'定义 Server Action- 表单用
action={fn}调用 - 事件处理直接
await fn() useActionState管理 pending 状态revalidatePath/revalidateTag/updateTag清除缓存redirect跳转页面- 务必验证身份和权限
下一章,我们来深入学习缓存机制。