
'use server' 指令
'use server' 指令
本 API 适用于 React 服务器组件。
'use server' 用于标记可从客户端代码调用的服务器函数。
参考 {#reference}
'use server' {#use-server}
在异步函数体的顶部添加 'use server',即可将该函数标记为可由客户端调用。我们称这些函数为服务器函数(Server Action)。
js {2}
async function addToCart(data) {
'use server';
// ...
}
当客户端调用服务器函数时,它会向服务器发起网络请求,并携带所有参数的序列化副本。如果服务器函数返回一个值,该值会被序列化并返回给客户端。
也可以将 'use server' 指令添加到文件顶部,而不必逐个标记函数。文件顶部的指令会将所有导出都标记为服务器函数,可在任何地方(包括客户端代码)导入和使用。
注意事项 {#caveats}
'use server'必须位于函数体或模块的顶部,且在任何导入或其他代码之前(代码顶部的注释是允许的)。它必须使用单引号或双引号编写,不能使用反引号。'use server'只能在服务器端文件中使用。生成的服务器函数可以通过 props 传递给客户端组件。请参阅支持的可序列化参数和返回值类型。- 要在客户端代码中导入服务器函数,必须在模块级别使用该指令。
- 由于底层的网络调用始终是异步的,
'use server'只能用于异步函数。 - 始终将服务器函数的参数视为不受信任的输入,并确保对任何数据修改操作进行权限校验。请参阅安全考虑。
- 服务器函数应在 transition 中调用。传递给
<form action>或formAction的服务器函数会被自动包装在 transition 中。 - 服务器函数专为更新服务器端状态的变更操作而设计,不建议用于数据获取。因此,实现服务器函数的框架通常一次只处理一个 Action,且没有对返回值进行缓存的能力。
安全考虑 {#security}
服务器函数的参数完全由客户端控制。出于安全考虑,请始终将它们视为不受信任的输入,并根据需要验证和转义参数。
在任何服务器函数中,请确保校验当前登录用户是否被允许执行该操作。
正在进行的工作
为防止敏感数据从服务器函数中被发送到客户端,React 提供了实验性的污染(taint)API,用于阻止唯一值和对象被传递给客户端代码。
请参阅 experimental_taintUniqueValue 与 experimental_taintObjectReference 了解更多。
可序列化参数和返回值 {#serializable-parameters-and-return-values}
当客户端代码通过网络调用服务器函数时,传递的任何参数都必须是可序列化的。
服务器函数参数支持以下类型:
特别注意,以下类型不受支持:
- React 元素或 JSX
- 函数,包括组件函数以及其他并非服务器函数的函数
- 类
- 任何类实例(上面列出的内置类除外),以及原型为
null的对象 - 未在全局注册的符号,例如
Symbol('my new symbol') - 事件处理程序中的事件对象
服务器函数支持的可序列化返回值,与客户端边界组件的可序列化 props 相同。
用法 {#usage}
表单中的服务器函数 {#server-actions-in-forms}
服务器函数最常见的用途是调用会变更数据的函数。在浏览器中,HTML form 元素 是用户提交变更的传统方式。借助 React 服务器组件,React 为表单中的服务器函数提供了一流支持。
下面是一个允许用户请求用户名的表单:
js {3}
// App.js
async function requestUsername(formData) {
'use server';
const username = formData.get('username');
// ...
}
export default function App() {
return (
<form action={requestUsername}>
<input type="text" name="username" />
<button type="submit">请求</button>
</form>
);
}
在此示例中,requestUsername 是一个传递给 <form> 的服务器函数。当用户提交此表单时,会向服务器函数 requestUsername 发起网络请求。当表单中的服务器函数被调用时,React 会将 FormData 作为第一个参数传给该函数。
通过将服务器函数传递给表单的 action,React 可以对表单进行逐步增强,这意味着表单可以在 JavaScript 包加载完成之前就实现提交。
处理表单中的返回值 {#handling-return-values}
在用户名请求表单中,可能会出现用户名不可用的情况,因此 requestUsername 应该告诉我们它是否失败。
可以使用 useActionState,根据服务器函数的返回结果来更新 UI,并支持逐步增强。
js
// requestUsername.js
'use server';
export default async function requestUsername(formData) {
const username = formData.get('username');
if (canRequest(username)) {
// ...
return '成功';
}
return '失败';
}
js {4,8}
// UsernameForm.js
'use client';
import { useActionState } from 'react';
import requestUsername from './requestUsername';
function UsernameForm() {
const [returnValue, action] = useActionState(requestUsername, null, 'n/a');
return (
<>
<form action={action}>
<input type="text" name="username" />
<button type="submit">请求</button>
</form>
<p>最后一次提交请求的返回值:{returnValue}</p>
</>
);
}
请注意,与大多数 Hook 一样,useActionState 只能在客户端代码中调用。
在 <form> 之外调用服务器函数 {#calling-a-server-action-outside-of-form}
服务器函数是暴露在服务器上的端点,可以在客户端代码的任何位置调用。
在 <form> 之外使用服务器函数时,请使用 transition,这样可以显示加载指示器、实现乐观状态更新,并处理意外错误。在表单中,服务器函数会被自动包装在 transition 中。
js {9-12}
import incrementLike from './actions';
import { useState, useTransition } from 'react';
function LikeButton() {
const [isPending, startTransition] = useTransition();
const [likeCount, setLikeCount] = useState(0);
const onClick = () => {
startTransition(async () => {
const currentCount = await incrementLike();
setLikeCount(currentCount);
});
};
return (
<>
<p>点赞数量:{likeCount}</p>
<button onClick={onClick} disabled={isPending}>点赞</button>
</>
);
}
js
// actions.js
'use server';
let likeCount = 0;
export default async function incrementLike() {
likeCount++;
return likeCount;
}
在调用处使用 await 获取返回的 Promise,即可读取服务器函数的返回值。
帮助我们改进文档
发现翻译问题或内容错误?请告诉我们。
