'use client' - This feature is available in the latest Canary

Canary

יש צורך ב-'use client' רק אם אתה משתמש ב-React רכיבי שרת או בונה ספרייה התואמת אליהם.

'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' מיובא מרכיב שרת, מחסנים תואמים יתייחסו לייבוא ​​המודול כגבול בין קוד הפעלה של שרת ל-client.

כתלות של RichTextEditor, formatDate וButton יוערכו גם על הלקוח ללא קשר אם המודולים שלהם מכילים הנחיה 'use client'. שים לב שניתן להעריך מודול בודד בשרת כשהוא מיובא מקוד שרת ובלקוח כשהוא מיובא מקוד לקוח.

אזהרות

  • 'use client' חייב להיות ממש בתחילת הקובץ, מעל כל ייבוא ​​או קוד אחר (הערות בסדר). הם חייבים להיות כתובים עם מרכאות בודדות או כפולות, אך לא עם סימנים אחוריים.
  • כאשר מודול 'use client' מיובא ממודול אחר שעובד על ידי לקוח, להנחיה אין השפעה.
  • כאשר מודול רכיב מכיל הנחיית 'use client', כל שימוש ברכיב זה מובטח להיות רכיב לקוח. עם זאת, עדיין ניתן להעריך רכיב בלקוח גם אם אין לו הנחיית 'use client'.
    • שימוש ברכיב נחשב רכיב לקוח אם הוא מוגדר במודול עם הנחיית 'use client' או כאשר מדובר בתלות טרנזיטיבית של מודול המכיל הוראת 'use client'. אחרת, זה רכיב שרת.
  • קוד המסומן להערכת לקוח אינו מוגבל לרכיבים. כל הקוד המהווה חלק מתת-עץ מודול הלקוח נשלח אל הלקוח ומופעל על ידי הלקוח.
  • כאשר מודול מוערך בשרת מייבא ערכים ממודול 'use client', הערכים חייבים להיות רכיב React או ערכי עזר הניתנים לסידרה נתמכים כדי לעבור לרכיב לקוח. כל מקרה אחר של use יגרום לחריגה.

כיצד 'use client' מסמן את קוד הלקוח

באפליקציית React, רכיבים מפוצלים לרוב לקבצים נפרדים, או מודולים.

עבור אפליקציות שuse 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>
    </>
  );
}

בעץ התלות במודול של אפליקציה לדוגמה זו, ההנחיה 'use client' ב-InspirationGenerator.js מסמנת את המודול הזה ואת כל התלות הטרנזיטיבית שלו כמודולים של לקוח. תת העץ שמתחיל ב-InspirationGenerator.js מסומן כעת כמודולי לקוח.

A tree graph with the top node representing the module 'App.js'. 'App.js' has three children: 'Copyright.js', 'FancyText.js', and 'InspirationGenerator.js'. 'InspirationGenerator.js' has two children: 'FancyText.js' and 'inspirations.js'. The nodes under and including 'InspirationGenerator.js' have a yellow background color to signify that this sub-graph is client-rendered due to the 'use client' directive in 'InspirationGenerator.js'.
A tree graph with the top node representing the module 'App.js'. 'App.js' has three children: 'Copyright.js', 'FancyText.js', and 'InspirationGenerator.js'. 'InspirationGenerator.js' has two children: 'FancyText.js' and 'inspirations.js'. The nodes under and including 'InspirationGenerator.js' have a yellow background color to signify that this sub-graph is client-rendered due to the 'use client' directive in 'InspirationGenerator.js'.

'use client' מפלח את עץ התלות של המודול של אפליקציית רכיבי השרת React, מסמן את InspirationGenerator.js ואת כל התלות שלו כמעובדים בלקוח.

במהלך הרינדור, המסגרת תעבד בשרת את רכיב השורש ותמשיך דרך עץ העיבוד, תבטל את הסכמתו להערכת כל קוד שיובא מקוד שסומן על ידי הלקוח.

החלק המעובד בשרת של עץ העיבוד נשלח לאחר מכן ללקוח. הלקוח, עם הורדת קוד הלקוח שלו, משלים את עיבוד שאר העץ.

A tree graph where each node represents a component and its children as child components. The top-level node is labelled 'App' and it has two child components 'InspirationGenerator' and 'FancyText'. 'InspirationGenerator' has two child components, 'FancyText' and 'Copyright'. Both 'InspirationGenerator' and its child component 'FancyText' are marked to be client-rendered.
A tree graph where each node represents a component and its children as child components. The top-level node is labelled 'App' and it has two child components 'InspirationGenerator' and 'FancyText'. 'InspirationGenerator' has two child components, 'FancyText' and 'Copyright'. Both 'InspirationGenerator' and its child component 'FancyText' are marked to be client-rendered.

עץ העיבוד של האפליקציה React רכיבי שרת. InspirationGenerator והרכיב הצאצא שלו FancyText הם רכיבים המיוצאים מקוד מסומן על ידי לקוח ונחשב כרכיבי לקוח.

אנו מציגים את ההגדרות הבאות:

  • רכיבי לקוח הם רכיבים בעץ רינדור המעובדים בלקוח.
  • רכיבי שרת הם רכיבים בעץ רינדור המעובדים בשרת.

בעבודה דרך האפליקציה לדוגמה, App, FancyText ו-Copyright הם כולם מעובדים בשרת ונחשבים לרכיבי שרת. מכיוון שInspirationGenerator.js והתלות הטרנזיטיבית שלו מסומנים כקוד לקוח, הרכיב InspirationGenerator והרכיב הצאצא שלו FancyText הם רכיבי לקוח.

Deep Dive

איך FancyText הוא גם שרת וגם רכיב לקוח?

לפי ההגדרות לעיל, הרכיב FancyText הוא גם רכיב שרת וגם רכיב לקוח, איך זה יכול להיות?

ראשית, נבהיר שהמונח “רכיב” אינו מדויק במיוחד. הנה רק שתי דרכים שבהן ניתן להבין “רכיב”:

  1. “רכיב” יכול להתייחס להגדרת רכיב. ברוב המקרים זו תהיה פונקציה.
// This is a definition of a component
function MyComponent() {
return <p>My Component</p>
}
  1. ”רכיב” יכול להתייחס גם ל-שימוש ברכיב בהגדרתו.
import MyComponent from './MyComponent';

function App() {
// This is a usage of a component
return <MyComponent />;
}

לעתים קרובות, חוסר הדיוק אינו חשוב כאשר מסבירים מושגים, אבל במקרה זה כן.

כאשר אנו מדברים על רכיבי שרת או לקוח, אנו מתייחסים לשימושים ברכיבים.

  • אם הרכיב מוגדר במודול עם הוראת 'use client', או שהרכיב מיובא ונקרא ב-Client Component, אז השימוש ברכיב הוא Client Component.
  • אחרת, השימוש ברכיב הוא רכיב שרת.
A tree graph where each node represents a component and its children as child components. The top-level node is labelled 'App' and it has two child components 'InspirationGenerator' and 'FancyText'. 'InspirationGenerator' has two child components, 'FancyText' and 'Copyright'. Both 'InspirationGenerator' and its child component 'FancyText' are marked to be client-rendered.
A tree graph where each node represents a component and its children as child components. The top-level node is labelled 'App' and it has two child components 'InspirationGenerator' and 'FancyText'. 'InspirationGenerator' has two child components, 'FancyText' and 'Copyright'. Both 'InspirationGenerator' and its child component 'FancyText' are marked to be client-rendered.
A render tree illustrates component usages.

בחזרה לשאלת FancyText, אנו רואים שלהגדרת הרכיב אין אין הנחיית 'use client' ויש לה שני שימושים.

השימוש ב-FancyText כילד של App, מסמן את השימוש הזה כרכיב שרת. כאשר FancyText מיובא ונקרא תחת InspirationGenerator, השימוש הזה ב-FancyText הוא רכיב לקוח שכן InspirationGenerator מכיל הנחיה 'use client'.

המשמעות היא שהגדרת הרכיב עבור FancyText תוערך בשרת וגם תוריד על ידי הלקוח כדי להציג את השימוש ברכיב הלקוח שלו.

Deep Dive

מכיוון שuse Copyright מוצג כילד של רכיב הלקוח InspirationGenerator, אתה עשוי להיות מופתע שזהו רכיב שרת.

נזכיר כי 'use client' מגדיר את הגבול בין קוד השרת והלקוח ב-עץ התלות של המודול, לא בעץ הרינדור.

A tree graph with the top node representing the module 'App.js'. 'App.js' has three children: 'Copyright.js', 'FancyText.js', and 'InspirationGenerator.js'. 'InspirationGenerator.js' has two children: 'FancyText.js' and 'inspirations.js'. The nodes under and including 'InspirationGenerator.js' have a yellow background color to signify that this sub-graph is client-rendered due to the 'use client' directive in 'InspirationGenerator.js'.
A tree graph with the top node representing the module 'App.js'. 'App.js' has three children: 'Copyright.js', 'FancyText.js', and 'InspirationGenerator.js'. 'InspirationGenerator.js' has two children: 'FancyText.js' and 'inspirations.js'. The nodes under and including 'InspirationGenerator.js' have a yellow background color to signify that this sub-graph is client-rendered due to the 'use client' directive in 'InspirationGenerator.js'.

'use client' מגדיר את הגבול בין קוד השרת ללקוח בעץ התלות של המודול.

בעץ התלות של המודול, אנו רואים שApp.js מייבא וקורא Copyright מהמודול Copyright.js. מכיוון שCopyright.js אינו מכיל הנחיה 'use client', השימוש ברכיב מוצג בשרת. App מוצג בשרת מכיוון שהוא רכיב השורש.

רכיבי לקוח יכולים לעבד רכיבי שרת מכיוון שuse אתה יכול להעביר את JSX בתור props. במקרה זה, InspirationGenerator מקבל Copyright בתור ילדים. עם זאת, מודול InspirationGenerator לעולם לא מייבא ישירות את מודול Copyright וגם לא קורא לרכיב, כל זה נעשה על ידי App. למעשה, הרכיב Copyright מבוצע במלואו לפני תחילת העיבוד של InspirationGenerator.

ההנחה היא שיחסי עיבוד הורה-ילדים בין רכיבים אינם מבטיחים את אותה סביבת עיבוד.

מתי use 'use client'

עם 'use client', אתה יכול לקבוע מתי רכיבים הם רכיבי לקוח. מכיוון שרכיבי שרת הם ברירת מחדל, הנה סקירה קצרה של היתרונות והמגבלות של רכיבי שרת כדי לקבוע מתי עליך לסמן משהו כלקוח מעובד.

לשם הפשטות, אנחנו מדברים על רכיבי שרת, אבל אותם עקרונות חלים על כל הקוד באפליקציה שלך שמופעל על ידי שרת.

היתרונות של רכיבי שרת

  • רכיבי שרת יכולים להפחית את כמות הקוד שנשלח ופועל על ידי הלקוח. רק מודולי לקוח מאגדים ומוערכים על ידי הלקוח.
  • רכיבי שרת נהנים מהפעלה על השרת. הם יכולים לגשת למערכת הקבצים המקומית ועשויים לחוות זמן אחזור נמוך עבור שליפות נתונים ובקשות רשת.

מגבלות של רכיבי שרת

  • רכיבי שרת אינם יכולים לתמוך באינטראקציה מכיוון שמטפלי אירועים חייבים להיות רשומים ומופעלים על ידי לקוח.
    • לדוגמה, ניתן להגדיר מטפלי אירועים כמו onClick רק ברכיבי לקוח.
  • רכיבי שרת אינם יכולים use רוב Hooks.
    • כאשר רכיבי שרת מעובדים, הפלט שלהם הוא בעצם רשימה של רכיבים עבור הלקוח לעיבוד. רכיבי שרת אינם נמשכים ב-memory לאחר העיבוד ואינם יכולים לקבל state משלהם.

סוגים ניתנים להסדרה המוחזרים על ידי רכיבי שרת

כמו בכל אפליקציה React, רכיבי אב מעבירים נתונים לרכיבי צאצא. מכיוון שהם מוצגים בסביבות שונות, העברת נתונים מרכיב שרת לרכיב לקוח דורשת שיקול נוסף.

ערכי עזר המועברים מרכיב שרת לרכיב לקוח חייבים להיות ניתנים לסידרה.

props הניתנים לסידרה כוללים:

יש לציין, אלה אינם נתמכים:

  • פונקציות שאינן מיוצאות ממודולים המסומנים על ידי לקוח או מסומנות באמצעות 'use server'
  • שיעורים
  • אובייקטים שהם מופעים של כל מחלקה (מלבד המובנים שהוזכרו) או אובייקטים עם אב טיפוס null
  • סמלים שאינם רשומים ברחבי העולם, למשל. Symbol('my new symbol')

שימוש

בניה עם אינטראקטיביות ו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' בחלק העליון.

לעומת זאת, רכיב שיוצר ממשק משתמש ללא אינטראקציה לא יצטרך להיות רכיב לקוח.

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' מכיוון שהוא אינו אינטראקטיבי ואינו use state. בנוסף, CounterContainer חייב להיות רכיב שרת כפי שהוא קורא ממערכת הקבצים המקומית בשרת, מה שמתאפשר רק ברכיב שרת.

ישנם גם רכיבים שאינם use תכונות שרת או לקוח בלבד ויכולים להיות אגנוסטיים למקום שבו הם מעבדים. בדוגמה הקודמת שלנו, 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 הוא used גם בתור שרת וגם כרכיב לקוח, תלוי איפה הוא מיובא וused.

אבל אם הפלט HTML של FancyText היה גדול יחסית לקוד המקור שלו (כולל תלות), ייתכן שיהיה יעיל יותר לאלץ אותו להיות תמיד רכיב לקוח. רכיבים שמחזירים מחרוזת נתיב SVG ארוכה הם מקרה אחד שבו עשוי להיות יעיל יותר לאלץ רכיב להיות רכיב לקוח.

שימוש ב-APIs של הלקוח

אפליקציית React שלך עשויה use client ספציפיים APIs, כגון APIs של הדפדפן עבור אחסון אינטרנט, מניפולציה של אודיו ווידאו וחומרת המכשיר, בין [אחרים] (https://developer.mozilla.org/en-US/docs/Web/API).

בדוגמה זו, הרכיב uses DOM APIs כדי לתפעל אלמנט canvas. מכיוון שאותם APIs זמינים רק בדפדפן, יש לסמן אותו כרכיב לקוח.

'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, תמנף ספריות של צד שלישי כדי לטפל בדפוסי ממשק משתמש נפוצים או בהיגיון.

ספריות אלו עשויות להסתמך על רכיב Hooks או APIs של לקוח. רכיבי צד שלישי שuse כל אחד מה-React APIs הבאים חייבים לפעול בלקוח:

אם ספריות אלו עודכנו כדי להיות תואמות לרכיבי שרת React, אז הם כבר יכללו סמני 'use client' משלהם, מה שיאפשר לך use אותם ישירות מרכיבי השרת שלך. אם ספרייה לא עודכנה, או אם רכיב צריך props כמו מטפלי אירועים שניתן לציין רק בלקוח, ייתכן שיהיה עליך להוסיף קובץ רכיב לקוח משלך בין רכיב לקוח של צד שלישי לרכיב השרת שלך שבו תרצה use אותו.