'use client'
可讓您標記在客戶端執行的程式碼。
參考
'use client'
在檔案的最上方,任何引入或其他程式碼之前(註解可以),加入 'use client'
來將模組及其遞移相依性標記為客戶端程式碼。
'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'
的檔案時,相容的打包器 會將模組引入視為伺服器執行程式碼和客戶端執行程式碼之間的界限。
作為 RichTextEditor
的相依性,formatDate
和 Button
也將在客戶端上進行評估,無論其模組是否包含 'use client'
指令。請注意,單個模組在從伺服器程式碼引入時可以在伺服器上評估,而在從客戶端程式碼引入時可以在客戶端上評估。
注意事項
'use client'
必須位於檔案的最開頭,在任何引入或其他程式碼之前(註解可以)。它們必須用單引號或雙引號括起來,但不能用反引號。- 當從另一個客戶端轉譯的模組引入
'use client'
模組時,該指令無效。 - 當元件模組包含
'use client'
指令時,該元件的任何用法都保證是客戶端元件。但是,即使元件沒有'use client'
指令,它仍然可以在客戶端上進行評估。- 如果元件用法定義在具有
'use client'
指令的模組中,或者當它是包含'use client'
指令的模組的遞移相依性時,則該元件用法被視為客戶端元件。否則,它就是伺服器元件。
- 如果元件用法定義在具有
- 標記為客戶端評估的程式碼不僅限於元件。屬於客戶端模組子樹的所有程式碼都會發送到客戶端並由客戶端執行。
- 當伺服器評估模組從一個 `
'use client'
模組導入值時,這些值必須是 React 元件或支援的可序列化屬性值,才能傳遞給客戶端元件。任何其他使用情況都會拋出例外。
`'use client'
如何標記客戶端程式碼
在 React 應用程式中,元件通常會分割成單獨的檔案,或稱為模組。
對於使用 React 伺服器元件的應用程式,預設情況下應用程式會進行伺服器渲染。`'use client'
在模組依賴樹中引入了伺服器-客戶端邊界,有效地建立了客戶端模組的子樹。
為了更好地說明這一點,請考慮以下 React 伺服器元件應用程式。
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
` 開始的子樹現在被標記為客戶端模組。


`'use client'
` 將 React 伺服器元件應用程式的模組依賴樹分段,將 `InspirationGenerator.js
` 及其所有依賴項標記為客戶端渲染。
在渲染期間,框架將會伺服器渲染根元件,並繼續通過渲染樹,選擇不評估從客戶端標記程式碼導入的任何程式碼。
然後將渲染樹的伺服器渲染部分發送到客戶端。客戶端下載其客戶端程式碼後,再完成渲染樹的其餘部分。


React 伺服器元件應用程式的渲染樹。`InspirationGenerator
` 及其子元件 `FancyText
` 是從客戶端標記程式碼匯出的元件,並被視為客戶端元件。
我們引入以下定義
- 客戶端元件是指在客戶端上渲染的渲染樹中的元件。
- 伺服器元件是指在伺服器上渲染的渲染樹中的元件。
通過範例應用程式,`App
`、`FancyText
` 和 `Copyright
` 都是伺服器渲染的,並被視為伺服器元件。由於 `InspirationGenerator.js
` 及其遞移依賴項被標記為客戶端程式碼,因此元件 `InspirationGenerator
` 及其子元件 `FancyText
` 是客戶端元件。
深入探討
`FancyText
` 如何既是伺服器元件又是客戶端元件?(連結圖示)
`FancyText
` 如何既是伺服器元件又是客戶端元件?(連結圖示)
根據上述定義,元件 `FancyText
` 既是伺服器元件又是客戶端元件,這怎麼可能?
首先,讓我們澄清一下「元件」這個術語並不是非常精確。這裡有兩種理解「元件」的方式
- 「元件」可以指元件定義。在大多數情況下,這將是一個函式。
// 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 />;
}
通常,在解釋概念時,這種不精確性並不重要,但在這種情況下卻很重要。
當我們談論伺服器元件或客戶端元件時,我們指的是元件用法。
- 如果元件是在具有 `
'use client'
指令的模組中定義的,或者元件是在客戶端元件中導入和呼叫的,則該元件用法是客戶端元件。 - 否則,元件用法是伺服器元件。


回到 `FancyText
` 的問題,我們看到元件定義*沒有* `'use client'
指令,並且它有兩種用法。
`FancyText
` 作為 `App
` 的子元件的用法,將該用法標記為伺服器元件。當 `FancyText
` 在 `InspirationGenerator
` 下導入和呼叫時,`FancyText
` 的該用法是客戶端元件,因為 `InspirationGenerator
` 包含 `'use client'
指令。
這意味著 `FancyText
` 的元件定義將在伺服器上進行評估,並且還會由客戶端下載以渲染其客戶端元件用法。
深入探討
為什麼 `Copyright
` 是伺服器元件?(連結圖示)
為什麼 `Copyright
` 是伺服器元件?(連結圖示)
因為 `Copyright
` 是作為客戶端元件 `InspirationGenerator
` 的子元件渲染的,您可能會驚訝於它是一個伺服器元件。
回想一下,`'use client'
` 定義了*模組依賴樹*上伺服器和客戶端程式碼之間的邊界,而不是渲染樹上的邊界。


`'use client'
` 定義了模組依賴樹上伺服器和客戶端程式碼之間的邊界。
在模組依賴關係樹狀圖中,我們可以看到 App.js
導入並呼叫了來自 Copyright.js
模組的 Copyright
。由於 Copyright.js
不包含 'use client'
指令,因此該元件的使用會在伺服器端渲染。 App
作為根元件也會在伺服器端渲染。
客戶端元件可以渲染伺服器端元件,因為您可以將 JSX 作為 props 傳遞。在這種情況下,InspirationGenerator
接收 Copyright
作為子元件。然而,InspirationGenerator
模組從未直接導入 Copyright
模組或呼叫該元件,所有這些操作都由 App
完成。實際上,Copyright
元件在 InspirationGenerator
開始渲染之前就已完全執行完畢。
重點是,元件之間的父子渲染關係並不保證它們在相同的渲染環境中。
何時使用 'use client'
使用 'use client'
,您可以決定哪些元件是客戶端元件。由於伺服器端元件是預設的,以下簡要概述伺服器端元件的優缺點,以幫助您判斷何時需要將某些內容標記為客戶端渲染。
為簡潔起見,我們討論的是伺服器端元件,但相同的原則適用於應用程式中所有在伺服器端執行的程式碼。
伺服器端元件的優點
- 伺服器端元件可以減少客戶端傳送和執行的程式碼量。只有客戶端模組才會被客戶端打包和評估。
- 伺服器端元件受益於在伺服器上運行。它們可以訪問本地檔案系統,並且在資料擷取和網路請求方面可能具有較低的延遲。
伺服器端元件的限制
- 伺服器端元件不支援互動,因為事件處理程式必須由客戶端註冊和觸發。
- 例如,像
onClick
這樣的事件處理程式只能在客戶端元件中定義。
- 例如,像
- 伺服器端元件不能使用大多數 Hooks。
- 當伺服器端元件被渲染時,它們的輸出本質上是供客戶端渲染的元件列表。伺服器端元件在渲染後不會駐留在記憶體中,也不能擁有自己的狀態。
伺服器端元件返回的可序列化類型
與任何 React 應用程式一樣,父元件會將資料傳遞給子元件。由於它們在不同的環境中渲染,因此將資料從伺服器端元件傳遞到客戶端元件需要額外考慮。
從伺服器端元件傳遞到客戶端元件的 Prop 值必須是可序列化的。
可序列化 props 包括
- 基本類型
- 字串 (string)
- 數字 (number)
- bigint
- 布林值 (boolean)
- undefined
- null
- symbol,僅限透過
Symbol.for
在全域 Symbol 註冊表中註冊的 symbol
- 包含可序列化值的迭代器
- 日期 (Date)
- 純 物件 (objects):使用 物件初始化式 (object initializers) 建立,具有可序列化屬性
- 是 伺服器函式 (Server Functions) 的函式
- 客戶端或伺服器端元件元素 (JSX)
- 承諾 (Promises)
值得注意的是,以下類型不支援
- 非從標記為客戶端的模組匯出或標記為
'use server'
的函式 (Functions) - 類別 (Classes)
- 任何類別(除了上述內建類別之外) 的實例物件,或者具有 null 原型 (a null prototype) 的物件
- 未在全域註冊的 Symbols,例如
Symbol('my new symbol')
用法
使用互動性及狀態建構
'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'
指令。
相反地,渲染 UI 而不需互動的組件則不需要是客戶端組件。
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
必須是伺服器組件,因為它會從伺服器上的本地檔案系統讀取資料,這只有在伺服器組件中才有可能。
也有一些組件不使用任何伺服器或僅限客戶端的特性,並且可以不區分渲染位置。在我們之前的範例中,FancyText
就是這樣一個組件。
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
可作為伺服器或客戶端組件使用,具體取決於其導入和使用的位置。
但是,如果 FancyText
的 HTML 輸出相對於其原始碼(包括依賴項)而言很大,則強制它始終作為客戶端組件可能會更有效率。返回長 SVG 路徑字串的組件是一種可能更有效率地強制組件成為客戶端組件的情況。
使用客戶端 API
你的 React 應用程式可能會使用客戶端特定的 API,例如瀏覽器的網頁儲存 API、音訊和視訊操作 API,以及裝置硬體 API,等等其他。
在此範例中,該組件使用 DOM API 來操作 canvas
元素。由於這些 API 僅在瀏覽器中可用,因此必須將其標記為客戶端組件。
'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} />;
}
使用第三方函式庫
在 React 應用程式中,你通常會利用第三方函式庫來處理常見的 UI 模式或邏輯。
這些函式庫可能依賴組件 Hook 或客戶端 API。使用以下任何 React API 的第三方組件必須在客戶端上運行
- createContext
react
和react-dom
Hooks,但不包括use
和useId
- forwardRef
- memo
- startTransition
- 如果它們使用客戶端 API,例如 DOM 插入或原生平台視圖
如果這些函式庫已更新為與 React 伺服器組件相容,則它們本身就已包含 'use client'
標記,讓你直接從伺服器組件中使用它們。如果函式庫尚未更新,或者組件需要只能在客戶端上指定的 props(例如事件處理程序),則可能需要在你想要使用它的第三方客戶端組件和伺服器組件之間添加自己的客戶端組件檔案。