'use client' 让你标记哪些代码在客户端运行。
参考
🌐 Reference
'use client'
在文件顶部添加 'use client' 以将该模块及其传递依赖标记为客户端代码。
🌐 Add 'use client' at the top of a file to mark the module and its transitive dependencies as client code.
'use client';
import { useState } from 'react';
import { formatDate } from './formatters';
import Button from './button';
export default function RichTextEditor({ timestamp, text }) {
const date = formatDate(timestamp);
// ...
const editButton = <Button />;
// ...
}当从服务器组件导入标记为 'use client' 的文件时,兼容的打包工具 会将模块导入视为服务器运行代码和客户端运行代码之间的边界。
🌐 When a file marked with 'use client' is imported from a Server Component, compatible bundlers will treat the module import as a boundary between server-run and client-run code.
作为 RichTextEditor 的依赖,formatDate 和 Button 也将无论其模块是否包含 'use client' 指令,都在客户端进行评估。请注意,当从服务器代码导入时,单个模块可能会在服务器上进行评估,而从客户端代码导入时,则会在客户端进行评估。
🌐 As dependencies of RichTextEditor, formatDate and Button will also be evaluated on the client regardless of whether their modules contain a 'use client' directive. Note that a single module may be evaluated on the server when imported from server code and on the client when imported from client code.
注意事项
🌐 Caveats
'use client'必须位于文件的最开头,在任何导入或其他代码之上(注释可以)。它们必须使用单引号或双引号书写,但不能使用反引号。- 当从另一个客户端渲染的模块导入
'use client'模块时,该指令不起作用。 - 当一个组件模块包含
'use client'指令时,该组件的任何使用都保证是客户端组件。然而,即使一个组件没有'use client'指令,它仍然可以在客户端进行评估。- 如果组件用法在包含
'use client'指令的模块中定义,或者是包含'use client'指令的模块的传递依赖,则该组件用法被视为客户端组件。否则,它是服务器组件。
- 如果组件用法在包含
- 标记为客户端评估的代码不限于组件。属于客户端模块子树的所有代码都会被发送给客户端并由客户端运行。
- 当服务器评估模块从
'use client'模块导入值时,这些值必须是 React 组件或 支持的可序列化 prop 值 才能传递给客户端组件。任何其他情况都会抛出异常。
'use client' 如何标记客户端代码
🌐 How 'use client' marks client code
在 React 应用中,组件通常被拆分到单独的文件或模块中。
🌐 In a React app, components are often split into separate files, or modules.
对于使用 React 服务器组件的应用,默认情况下应用是由服务器渲染的。'use client' 在 模块依赖树 中引入了一个服务器-客户端边界,有效地创建了一个客户端模块的子树。
🌐 For apps that use React Server Components, the app is server-rendered by default. 'use client' introduces a server-client boundary in the module dependency tree, effectively creating a subtree of Client modules.
为了更好地说明这一点,请考虑以下 React Server Components 应用。
🌐 To better illustrate this, consider the following React Server Components app.
import FancyText from './FancyText'; import InspirationGenerator from './InspirationGenerator'; import Copyright from './Copyright'; export default function App() { return ( <> <FancyText title text="Get Inspired App" /> <InspirationGenerator> <Copyright year={2004} /> </InspirationGenerator> </> ); }
在此示例应用的模块依赖树中,InspirationGenerator.js 中的 'use client' 指令将该模块及其所有传递依赖标记为客户端模块。从 InspirationGenerator.js 开始的子树现在被标记为客户端模块。
🌐 In the module dependency tree of this example app, the 'use client' directive in InspirationGenerator.js marks that module and all of its transitive dependencies as Client modules. The subtree starting at InspirationGenerator.js is now marked as Client modules.


'use client' 对 React 服务器组件应用的模块依赖树进行分割,将 InspirationGenerator.js 及其所有依赖标记为客户端渲染。
在渲染过程中,框架将对根组件进行服务端渲染,并继续遍历渲染树,选择不评估从客户端标记的代码中导入的任何代码。
🌐 During render, the framework will server-render the root component and continue through the render tree, opting-out of evaluating any code imported from client-marked code.
渲染树的服务器渲染部分随后被发送到客户端。客户端在下载了其客户端代码后,然后完成剩余部分的渲染。
🌐 The server-rendered portion of the render tree is then sent to the client. The client, with its client code downloaded, then completes rendering the rest of the tree.


React 服务器组件应用的渲染树。InspirationGenerator 及其子组件 FancyText 是从标记为客户端的代码中导出的组件,并被视为客户端组件。
我们引入以下定义:
🌐 We introduce the following definitions:
- 客户端组件 是指在渲染树中在客户端渲染的组件。
- 服务器组件 是渲染树中在服务器上渲染的组件。
通过示例应用,App、FancyText 和 Copyright 都是服务器渲染的,并被视为服务器组件。由于 InspirationGenerator.js 及其传递依赖标记为客户端代码,组件 InspirationGenerator 及其子组件 FancyText 是客户端组件。
🌐 Working through the example app, App, FancyText and Copyright are all server-rendered and considered Server Components. As InspirationGenerator.js and its transitive dependencies are marked as client code, the component InspirationGenerator and its child component FancyText are Client Components.
深入研究
🌐 How is FancyText both a Server and a Client Component?
根据上述定义,组件 FancyText 同时是服务器组件和客户端组件,这怎么可能?
🌐 By the above definitions, the component FancyText is both a Server and Client Component, how can that be?
首先,让我们明确一下,“组件”这个术语并不是非常精确。“组件”可以理解的方式这里只列出两种:
🌐 First, let’s clarify that the term “component” is not very precise. Here are just two ways “component” can be understood:
- “组件”可以指一个组件定义。在大多数情况下,这将是一个函数。
// This is a definition of a component
function MyComponent() {
return <p>My Component</p>
}- “组件”也可以指其定义的组件用法。
import MyComponent from './MyComponent';
function App() {
// This is a usage of a component
return <MyComponent />;
}通常,在解释概念时,不精确性并不重要,但在这种情况下却很重要。
🌐 Often, the imprecision is not important when explaining concepts, but in this case it is.
当我们谈论服务器或客户端组件时,我们指的是组件的使用。
🌐 When we talk about Server or Client Components, we are referring to component usages.
- 如果组件在具有
'use client'指令的模块中定义,或者组件被导入并在客户端组件中调用,那么该组件的使用就是客户端组件。 - 否则,该组件使用是服务器组件。


回到关于 FancyText 的问题,我们看到组件定义 没有 'use client' 指令,并且它有两个用法。
🌐 Back to the question of FancyText, we see that the component definition does not have a 'use client' directive and it has two usages.
FancyText 作为 App 的子项的使用,将该使用标记为服务器组件。当 FancyText 被导入并在 InspirationGenerator 下调用时,FancyText 的该使用是客户端组件,因为 InspirationGenerator 包含一个 'use client' 指令。
🌐 The usage of FancyText as a child of App, marks that usage as a Server Component. When FancyText is imported and called under InspirationGenerator, that usage of FancyText is a Client Component as InspirationGenerator contains a 'use client' directive.
这意味着 FancyText 的组件定义将会在服务器端进行评估,同时也会被客户端下载以渲染其客户端组件的使用。
🌐 This means that the component definition for FancyText will both be evaluated on the server and also downloaded by the client to render its Client Component usage.
深入研究
🌐 Why is Copyright a Server Component?
因为 Copyright 被渲染为客户端组件 InspirationGenerator 的子组件,你可能会惊讶于它是一个服务端组件。
🌐 Because Copyright is rendered as a child of the Client Component InspirationGenerator, you might be surprised that it is a Server Component.
请记住,'use client' 定义的是 模块依赖树 上服务器代码和客户端代码的边界,而不是渲染树上的边界。
🌐 Recall that 'use client' defines the boundary between server and client code on the module dependency tree, not the render tree.


'use client' 定义了模块依赖树上服务器代码与客户端代码的分界。
在模块依赖树中,我们看到 App.js 从 Copyright.js 模块导入并调用 Copyright。由于 Copyright.js 不包含 'use client' 指令,该组件的使用在服务器上渲染。App 在服务器上渲染,因为它是根组件。
🌐 In the module dependency tree, we see that App.js imports and calls Copyright from the Copyright.js module. As Copyright.js does not contain a 'use client' directive, the component usage is rendered on the server. App is rendered on the server as it is the root component.
客户端组件可以渲染服务器组件,因为你可以将 JSX 作为 props 传递。在这种情况下,InspirationGenerator 接收 Copyright 作为 children。然而,InspirationGenerator 模块从未直接导入 Copyright 模块,也没有调用该组件,这一切都是由 App 完成的。事实上,Copyright 组件在 InspirationGenerator 开始渲染之前已经完全执行。
🌐 Client Components can render Server Components because you can pass JSX as props. In this case, InspirationGenerator receives Copyright as children. However, the InspirationGenerator module never directly imports the Copyright module nor calls the component, all of that is done by App. In fact, the Copyright component is fully executed before InspirationGenerator starts rendering.
结论是组件之间的父子渲染关系不能保证相同的渲染环境。
🌐 The takeaway is that a parent-child render relationship between components does not guarantee the same render environment.
何时使用 'use client'
🌐 When to use 'use client'
使用 'use client',你可以确定组件何时是客户端组件。由于服务器组件是默认的,这里简要概述了服务器组件的优点和局限性,以帮助你确定何时需要将某些内容标记为客户端渲染。
🌐 With 'use client', you can determine when components are Client Components. As Server Components are default, here is a brief overview of the advantages and limitations to Server Components to determine when you need to mark something as client rendered.
为了简单起见,我们讨论服务器组件,但相同的原则适用于服务器运行的应用中的所有代码。
🌐 For simplicity, we talk about Server Components, but the same principles apply to all code in your app that is server run.
服务器组件的优点
🌐 Advantages of Server Components
- 服务器组件可以减少客户端发送和运行的代码量。只有客户端模块会被客户端打包和执行。
- 服务器组件从在服务器上运行中受益。它们可以访问本地文件系统,并且在数据获取和网络请求时可能会体验到低延迟。
服务器组件的限制
🌐 Limitations of Server Components
- 服务器组件无法支持交互,因为事件处理程序必须由客户端注册和触发。
- 例如,像
onClick这样的事件处理程序只能在客户端组件中定义。
- 例如,像
- 服务器组件无法使用大多数 Hook。
- 当服务器组件被渲染时,它们的输出本质上是供客户端渲染的组件列表。服务器组件在渲染后不会保存在内存中,也不能拥有自己的状态。
服务器组件返回的可序列化类型
🌐 Serializable types returned by Server Components
与任何 React 应用一样,父组件将数据传递给子组件。由于它们在不同的环境中渲染,从服务器组件向客户端组件传递数据需要额外的考虑。
🌐 As in any React app, parent components pass data to child components. As they are rendered in different environments, passing data from a Server Component to a Client Component requires extra consideration.
从服务器组件传递到客户端组件的属性值必须是可序列化的。
🌐 Prop values passed from a Server Component to Client Component must be serializable.
可序列化的属性包括:
🌐 Serializable props include:
值得注意的是,这些不受支持:
🌐 Notably, these are not supported:
- Functions 未从客户端标记的模块导出或未标记为
'use server' - 课程
- 属于任何类的实例的对象(除了提到的内置类)或具有空原型的对象
- 符号未在全局注册,例如
Symbol('my new symbol')
用法
🌐 Usage
具有交互性和状态的构建
🌐 Building with interactivity and state
'use client'; import { useState } from 'react'; export default function Counter({initialValue = 0}) { const [countValue, setCountValue] = useState(initialValue); const increment = () => setCountValue(countValue + 1); const decrement = () => setCountValue(countValue - 1); return ( <> <h2>Count Value: {countValue}</h2> <button onClick={increment}>+1</button> <button onClick={decrement}>-1</button> </> ); }
由于 Counter 既需要 useState Hook 又需要事件处理程序来增加或减少值,因此该组件必须是客户端组件,并且在顶部需要 'use client' 指令。
🌐 As Counter requires both the useState Hook and event handlers to increment or decrement the value, this component must be a Client Component and will require a 'use client' directive at the top.
相反,无需交互即可渲染 UI 的组件不需要是客户端组件。
🌐 In contrast, a component that renders UI without interaction will not need to be a Client Component.
import { readFile } from 'node:fs/promises';
import Counter from './Counter';
export default async function CounterContainer() {
const initialValue = await readFile('/path/to/counter_value');
return <Counter initialValue={initialValue} />
}例如,Counter的父组件CounterContainer不需要'use client',因为它不是交互式的,并且不使用状态。此外,CounterContainer必须是服务器组件,因为它从服务器上的本地文件系统读取数据,而这只有在服务器组件中才可能实现。
🌐 For example, Counter’s parent component, CounterContainer, does not require 'use client' as it is not interactive and does not use state. In addition, CounterContainer must be a Server Component as it reads from the local file system on the server, which is possible only in a Server Component.
还有一些组件不使用任何服务器或客户端专有的功能,并且可以对它们渲染的位置保持中立。在我们之前的例子中,FancyText 就是这样一个组件。
🌐 There are also components that don’t use any server or client-only features and can be agnostic to where they render. In our earlier example, FancyText is one such component.
export default function FancyText({title, text}) {
return title
? <h1 className='fancy title'>{text}</h1>
: <h3 className='fancy cursive'>{text}</h3>
}在这种情况下,我们不添加 'use client' 指令,因此当从服务器组件引用时,FancyText 的 输出(而不是其源代码)会被发送到浏览器。如之前的 Inspirations 应用示例所示,FancyText 可以根据其被导入和使用的位置,既作为服务器组件也作为客户端组件使用。
🌐 In this case, we don’t add the 'use client' directive, resulting in FancyText’s output (rather than its source code) to be sent to the browser when referenced from a Server Component. As demonstrated in the earlier Inspirations app example, FancyText is used as both a Server or Client Component, depending on where it is imported and used.
但是,如果 FancyText 的 HTML 输出相对于其源码(包括依赖)来说很大,那么强制其始终作为客户端组件可能更高效。返回长 SVG 路径字符串的组件就是这种情况下,强制组件成为客户端组件可能更高效的一种情况。
🌐 But if FancyText’s HTML output was large relative to its source code (including dependencies), it might be more efficient to force it to always be a Client Component. Components that return a long SVG path string are one case where it may be more efficient to force a component to be a Client Component.
使用客户端 API
🌐 Using client APIs
你的 React 应用可能会使用特定于客户端的 API,例如用于网页存储、音频和视频操作以及设备硬件的浏览器 API,等等 others 。
🌐 Your React app may use client-specific APIs, such as the browser’s APIs for web storage, audio and video manipulation, and device hardware, among others.
在此示例中,该组件使用 DOM APIs 来操作 canvas 元素。由于这些 API 仅在浏览器中可用,因此它必须被标记为客户端组件。
🌐 In this example, the component uses DOM APIs to manipulate a canvas element. Since those APIs are only available in the browser, it must be marked as a Client Component.
'use client';
import {useRef, useEffect} from 'react';
export default function Circle() {
const ref = useRef(null);
useLayoutEffect(() => {
const canvas = ref.current;
const context = canvas.getContext('2d');
context.reset();
context.beginPath();
context.arc(100, 75, 50, 0, 2 * Math.PI);
context.stroke();
});
return <canvas ref={ref} />;
}使用第三方库
🌐 Using third-party libraries
通常在 React 应用中,你将利用第三方库来处理常见的 UI 模式或逻辑。
🌐 Often in a React app, you’ll leverage third-party libraries to handle common UI patterns or logic.
这些库可能依赖于组件 Hooks 或客户端 API。使用以下任意 React API 的第三方组件必须在客户端运行:
🌐 These libraries may rely on component Hooks or client APIs. Third-party components that use any of the following React APIs must run on the client:
- createContext
react和react-dom钩子,不包括use和useId- forwardRef
- memo
- 开始过渡
- 如果他们使用客户端 API,例如 DOM 插入或原生平台视图
如果这些库已经更新以兼容 React 服务器组件,那么它们将已经包含自己的 'use client' 标记,允许你直接从服务器组件中使用它们。如果一个库尚未更新,或者某个组件需要像事件处理器这样的只能在客户端指定的 props,你可能需要在第三方客户端组件和你希望使用它的服务器组件之间添加你自己的客户端组件文件。
🌐 If these libraries have been updated to be compatible with React Server Components, then they will already include 'use client' markers of their own, allowing you to use them directly from your Server Components. If a library hasn’t been updated, or if a component needs props like event handlers that can only be specified on the client, you may need to add your own Client Component file in between the third-party Client Component and your Server Component where you’d like to use it.