'use server' 标记可以从客户端代码调用的服务器端函数。
参考
🌐 Reference
'use server'
在异步函数体的顶部添加 'use server',以将该函数标记为客户端可调用的函数。我们将这些函数称为 服务器函数。
🌐 Add 'use server' at the top of an async function body to mark the function as callable by the client. We call these functions Server Functions.
async function addToCart(data) {
'use server';
// ...
}在客户端调用服务器函数时,它会向服务器发出网络请求,其中包含传递的任何参数的序列化副本。如果服务器函数返回一个值,该值将被序列化并返回给客户端。
🌐 When calling a Server Function on the client, it will make a network request to the server that includes a serialized copy of any arguments passed. If the Server Function returns a value, that value will be serialized and returned to the client.
与其单独用 'use server' 标记每个函数,不如将该指令添加到文件顶部,以将该文件中的所有导出标记为服务器函数,这些函数可以在任何地方使用,包括在客户端代码中导入。
🌐 Instead of individually marking functions with 'use server', you can add the directive to the top of a file to mark all exports within that file as Server Functions that can be used anywhere, including imported in client code.
注意事项
🌐 Caveats
'use server'必须位于其函数或模块的最开始位置;在任何其他代码之上,包括导入语句(指令上方的注释是可以的)。它们必须使用单引号或双引号书写,不能使用反引号。'use server'只能在服务器端文件中使用。生成的服务器函数可以通过 props 传递给客户端组件。请参见支持的 序列化类型。- 要从 客户端代码 导入服务器函数,必须在模块层级使用该指令。
- 由于底层的网络调用总是异步的,
'use server'只能在异步函数中使用。 - 始终将传递给服务器函数的参数视为不可信的输入,并授权任何更改。请参阅安全注意事项。
- 服务器函数应该在 Transition 中调用。传递给
<form action>或formAction的服务器函数将自动在过渡中调用。 - 服务器函数旨在执行会更新服务器端状态的变更;它们不推荐用于数据获取。因此,实现服务器函数的框架通常一次只处理一个操作,并且没有缓存返回值的方法。
安全考虑
🌐 Security considerations
传递给服务器函数的参数完全由客户端控制。出于安全考虑,应始终将其视为不可信的输入,并确保对参数进行适当的验证和转义。
🌐 Arguments to Server Functions are fully client-controlled. For security, always treat them as untrusted input, and make sure to validate and escape arguments as appropriate.
在任何服务器函数中,请确保验证已登录用户是否被允许执行该操作。
🌐 In any Server Function, make sure to validate that the logged-in user is allowed to perform that action.
可序列化的参数和返回值
🌐 Serializable arguments and return values
由于客户端代码通过网络调用服务器函数,因此传递的任何参数都需要可序列化。
🌐 Since client code calls the Server Function over the network, any arguments passed will need to be serializable.
以下是服务器函数参数支持的类型:
🌐 Here are supported types for Server Function arguments:
值得注意的是,这些不受支持:
🌐 Notably, these are not supported:
- React 元素,或 JSX
- 函数,包括组件函数或任何其他非服务器函数的函数
- 课程
- 属于任何类的实例的对象(除了提到的内置类)或具有空原型的对象
- 符号未在全局注册,例如
Symbol('my new symbol') - 来自事件处理程序的事件
支持的可序列化返回值与边界客户端组件的可序列化属性相同。
🌐 Supported serializable return values are the same as serializable props for a boundary Client Component.
用法
🌐 Usage
表单中的服务器函数
🌐 Server Functions in forms
Server Functions 最常见的用例是调用会修改数据的函数。在浏览器中,HTML 表单元素 是用户提交修改的传统方法。借助 React 服务器组件,React 在表单中引入了对作为操作的 Server Functions 的一流支持。
🌐 The most common use case of Server Functions will be calling functions that mutate data. On the browser, the HTML form element is the traditional approach for a user to submit a mutation. With React Server Components, React introduces first-class support for Server Functions as Actions in forms.
这是一个允许用户请求用户名的表单。
🌐 Here is a form that allows a user to request a username.
// 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">Request</button>
</form>
);
}在这个例子中,requestUsername 是一个传递给 <form> 的服务器函数。当用户提交此表单时,会向服务器函数 requestUsername 发出网络请求。在表单中调用服务器函数时,React 会将表单的 FormData 作为第一个参数传递给服务器函数。
通过将一个服务器函数传递给表单 action,React 可以渐进增强该表单。这意味着表单可以在 JavaScript 包加载之前提交。
🌐 By passing a Server Function to the form action, React can progressively enhance the form. This means that forms can be submitted before the JavaScript bundle is loaded.
处理表单中的返回值
🌐 Handling return values in forms
在用户名请求表单中,可能会出现用户名不可用的情况。requestUsername 应该告诉我们它是否失败。
🌐 In the username request form, there might be the chance that a username is not available. requestUsername should tell us if it fails or not.
要在支持渐进增强的同时根据服务器函数的结果更新用户界面,请使用 useActionState。
🌐 To update the UI based on the result of a Server Function while supporting progressive enhancement, use useActionState.
// requestUsername.js
'use server';
export default async function requestUsername(formData) {
const username = formData.get('username');
if (canRequest(username)) {
// ...
return 'successful';
}
return 'failed';
}// UsernameForm.js
'use client';
import { useActionState } from 'react';
import requestUsername from './requestUsername';
function UsernameForm() {
const [state, action] = useActionState(requestUsername, null, 'n/a');
return (
<>
<form action={action}>
<input type="text" name="username" />
<button type="submit">Request</button>
</form>
<p>Last submission request returned: {state}</p>
</>
);
}请注意,像大多数 Hook 一样,useActionState 只能在 客户端代码中调用。
在 <form> 外调用服务器函数
🌐 Calling a Server Function outside of <form>
服务器函数是公开的服务器端点,可以在客户端代码中的任何位置调用。
🌐 Server Functions are exposed server endpoints and can be called anywhere in client code.
在表单(form)之外使用服务器函数时,在过渡中调用服务器函数,这允许你显示加载指示器、展示乐观状态更新以及处理意外错误。表单将自动在过渡中封装服务器函数。
🌐 When using a Server Function outside a form, call the Server Function in a Transition, which allows you to display a loading indicator, show optimistic state updates, and handle unexpected errors. Forms will automatically wrap Server Functions in transitions.
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>Total Likes: {likeCount}</p>
<button onClick={onClick} disabled={isPending}>Like</button>;
</>
);
}// actions.js
'use server';
let likeCount = 0;
export default async function incrementLike() {
likeCount++;
return likeCount;
}要读取服务器函数的返回值,你需要对返回的 promise 进行 await。
🌐 To read a Server Function return value, you’ll need to await the promise returned.