服务器操作和数据变更
服务器操作是在服务器上执行的异步函数。它们可以在服务器组件和客户端组件中调用,用于处理 Next.js 应用程序中的表单提交和数据变更。
🎥 观看: 了解更多关于使用服务器操作进行数据变更的内容 → YouTube (10 分钟)。
可以使用 React 的 "use server"
指令定义服务器操作。你可以将该指令放在 async
函数的顶部,将该函数标记为服务器操作,或者将其放在单独文件的顶部,将该文件的所有导出标记为服务器操作。
服务器组件可以使用内联函数级别或模块级别的 "use server"
指令。要内联服务器操作,请将 "use server"
添加到函数体的顶部:
要在客户端组件中调用服务器操作,创建一个新文件并在其顶部添加 "use server"
指令。该文件中的所有函数都将被标记为可在客户端和服务器组件中重用的服务器操作:
你还可以将服务器操作作为 prop 传递给客户端组件:
通常,Next.js TypeScript 插件会标记 client-component.tsx
中的 updateItemAction
,因为它是一个函数,通常无法跨客户端-服务器边界序列化。
然而,名为 action
或以 Action
结尾的 props 被假定为接收服务器操作。
这只是一个启发式方法,因为 TypeScript 插件实际上不知道它接收的是服务器操作还是普通函数。
运行时类型检查仍然会确保你不会意外地将函数传递给客户端组件。
- 服务器操作可以使用
<form>
元素中的 action
属性调用:
- 服务器组件默认支持渐进增强,这意味着即使 JavaScript 尚未加载或被禁用,表单也会被提交。
- 在客户端组件中,如果 JavaScript 尚未加载,调用服务器操作的表单将排队提交,优先进行客户端水合。
- 水合后,浏览器在表单提交时不会刷新。
- 服务器操作不限于
<form>
,可以从事件处理程序、useEffect
、第三方库以及其他表单元素(如 <button>
)中调用。
- 服务器操作与 Next.js 缓存和重新验证架构集成。当调用操作时,Next.js 可以在单次服务器往返中返回更新的 UI 和新数据。
- 在幕后,操作使用
POST
方法,只有这种 HTTP 方法可以调用它们。
- 服务器操作的参数和返回值必须是 React 可序列化的。请参阅 React 文档中的可序列化参数和返回值列表。
- 服务器操作是函数。这意味着它们可以在应用程序的任何地方重复使用。
- 服务器操作继承它们所在页面或布局的运行时。
- 服务器操作继承它们所在页面或布局的路由段配置,包括
maxDuration
等字段。
React 扩展了 HTML <form>
元素,允许使用 action
属性调用服务器操作。
在表单中调用时,操作会自动接收 FormData
对象。你不需要使用 React 的 useState
来管理字段,而是可以使用原生的 FormData
方法提取数据:
需要了解的要点:
你可以使用 JavaScript 的 bind
方法向服务器操作传递额外参数。
服务器操作将接收 userId
参数,以及表单数据:
需要了解的要点:
- 另一种方法是将参数作为表单中的隐藏输入字段传递(例如
<input type="hidden" name="userId" value={userId} />
)。但是,该值将成为渲染的 HTML 的一部分,不会被编码。
.bind
在服务器和客户端组件中都可以使用。它还支持渐进增强。
你还可以在 <form>
内部嵌套的元素(如 <button>
、<input type="submit">
和 <input type="image">
)中调用服务器操作。这些元素接受 formAction
属性或事件处理程序。
这在需要在表单中调用多个服务器操作的情况下非常有用。例如,你可以创建一个特定的 <button>
元素来保存文章草稿,除了发布它之外。有关更多信息,请参阅 React <form>
文档。
你可以使用 requestSubmit()
方法以编程方式触发表单提交。例如,当用户使用 ⌘
+ Enter
键盘快捷键提交表单时,你可以监听 onKeyDown
事件:
这将触发最近的 <form>
祖先的提交,从而调用服务器操作。
我们建议使用 HTML 验证(如 required
和 type="email"
)进行基本的客户端表单验证。
对于更高级的服务器端验证,你可以使用像 zod 这样的库来验证表单字段,然后再变更数据:
一旦在服务器上验证了字段,你可以在操作中返回一个可序列化的对象,并使用 React 的 [useActionState
](https://react.dev/```jsx
reference/react/useActionState) hook 向用户显示消息。
- 通过将操作传递给
useActionState
,操作的函数签名会发生变化,接收一个新的 prevState
或 initialState
参数作为其第一个参数。
useActionState
是一个 React hook,因此必须在客户端组件中使用。
然后,你可以将你的操作传递给 useActionState
hook,并使用返回的 state
来显示错误消息。
需要了解的要点:
- 在变更数据之前,你应该始终确保用户有权执行该操作。请参阅身份验证和授权。
- 在早期的 React Canary 版本中,这个 API 是 React DOM 的一部分,称为
useFormState
。
useActionState
hook 暴露了一个 pending
状态,可用于在执行操作时显示加载指示器。
需要了解的要点: 另外,你还可以使用 useFormStatus
hook 为特定表单显示等待状态。
你可以使用 React 的 useOptimistic
hook 在服务器操作执行完成之前乐观地更新 UI,而不是等待响应:
虽然在 <form>
元素中使用服务器操作很常见,但它们也可以通过事件处理程序(如 onClick
)调用。例如,要增加点赞数:
你还可以向表单元素添加事件处理程序,例如,在 onChange
时保存表单字段:
对于这种情况,多个事件可能会快速连续触发,我们建议去抖动以防止不必要的服务器操作调用。
你可以使用 React 的 useEffect
hook 在组件挂载或依赖项更改时调用服务器操作。这对于依赖全局事件或需要自动触发的变更很有用。例如,onKeyDown
用于应用快捷键,用于无限滚动的交叉观察器 hook,或者在组件挂载时更新查看次数:
请记住考虑 useEffect
的行为和注意事项。
当抛出错误时,它将被客户端上最近的 error.js
或 <Suspense>
边界捕获。我们建议使用 try/catch
来返回错误,以便由你的 UI 处理。
例如,你的服务器操作可能通过返回一条消息来处理创建新项目时的错误:
需要了解的要点:
你可以使用 revalidatePath
API 在服务器操作内重新验证 Next.js 缓存:
或者使用 revalidateTag
使用缓存标签使特定的数据获取失效:
如果你想在服务器操作完成后将用户重定向到不同的路由,你可以使用 redirect
API。redirect
需要在 try/catch
块之外调用:
你可以使用 cookies
API 在服务器操作中 get
、set
和 delete
cookie:
查看其他示例以了解如何从服务器操作中删除 cookie。
你应该像处理公开的 API 端点一样处理服务器操作,并确保用户有权执行该操作。例如:
在组件内定义服务器操作会创建一个闭包,其中操作可以访问外部函数的作用域。例如,publish
操作可以访问 publishVersion
变量:
当你需要在渲染时捕获数据的快照(例如 publishVersion
)以便稍后在调用操作时使用时,闭包很有用。
然而,为了实现这一点,捕获的变量会被发送到客户端,然后在调用操作时再发送回服务器。为了防止敏感数据暴露给客户端,Next.js 会自动加密闭包变量。每次构建 Next.js 应用程序时,都会为每个操作生成一个新的私钥。这意味着操作只能针对特定的构建调用。
需要了解的要点: 我们不建议仅依靠加密来防止敏感值暴露在客户端。相反,你应该使用 React taint APIs 主动防止特定数据被发送到客户端。
当在多个服务器上自托管 Next.js 应用程序时,每个服务器实例可能最终会有不同的加密密钥,导致潜在的不一致。
为了缓解这个问题,你可以使用 process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY
环境变量覆盖加密密钥。指定此变量可确保你的加密密钥在构建之间保持一致,并且所有服务器实例使用相同的密钥。
这是一个高级用例,其中跨多个部署的一致加密行为对你的应用程序至关重要。你应该考虑标准的安全实践,如密钥轮换和签名。
需要了解的要点: 部署到 Vercel 的 Next.js 应用程序会自动处理这个问题。
由于服务器操作可以在 <form>
元素中调用,这使它们容易受到 CSRF 攻击。
在幕后,服务器操作使用 POST
方法,并且只有这种 HTTP 方法被允许调用它们。这可以防止现代浏览器中的大多数 CSRF 漏洞,特别是考虑到 SameSite cookie 是默认设置。
作为额外的保护,Next.js 中的服务器操作还会比较 Origin 标头和 Host 标头(或 X-Forwarded-Host
)。如果这些不匹配,请求将被中止。换句话说,服务器操作只能在托管它的页面的相同主机上调用。
对于使用反向代理或多层后端架构的大型应用程序(其中服务器 API 与生产域不同),建议使用配置选项 serverActions.allowedOrigins
来指定安全来源列表。该选项接受一个字符串数组。
了解更多关于安全性和服务器操作的信息。
有关服务器操作的更多信息,请查看以下 React 文档: