Spec-Zone.ru › React

'use client'

Компоненты React на сервере

'use client' предназначен для использования с компонентами React на сервере.

'use client' позволяет помечать код, который выполняется на клиенте.

  • Справочник
    • 'use client'
    • Как 'use client' помечает код для клиента
    • Когда использовать 'use client'
    • Типы данных, возвращаемые серверными компонентами и сериализуемые
  • Использование
    • Разработка с интерактивностью и состоянием
    • Использование API клиента
    • Использование библиотек сторонних разработчиков

Справочник

'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>
    </>
  );
}

В дереве зависимостей модулей этого примера директива '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'.

'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.

Дерево рендеринга приложения React с компонентами на сервере. InspirationGenerator и его дочерний компонент FancyText являются компонентами, экспортированными из помеченного клиентским кодом и считаются клиентскими компонентами.

Мы вводим следующие определения:

  • Клиентские компоненты — это компоненты в дереве рендеринга, которые рендерятся на клиенте.
  • Серверные компоненты — это компоненты в дереве рендеринга, которые рендерятся на сервере.

Работая с примером приложения, App, FancyText и Copyright все рендерятся на сервере и считаются серверными компонентами. Поскольку InspirationGenerator.js и его транзитивные зависимости помечены как клиентский код, компонент InspirationGenerator и его дочерний компонент FancyText являются клиентскими компонентами.

Подробный анализ

Как 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', или компонент импортирован и вызван в клиентском компоненте, то использование компонента является клиентским компонентом.
  • В противном случае использование компонента является серверным компонентом.
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.
Дерево рендеринга иллюстрирует использование компонента.

Вернёмся к вопросу о FancyText, мы видим, что определение компонента не содержит директивы 'use client' и имеет два использования.

Использование FancyText в качестве дочернего компонента App помечает это использование как серверный компонент. Когда FancyText импортирован и вызван под InspirationGenerator, это использование FancyText является клиентским компонентом, так как InspirationGenerator содержит директиву 'use client'.

Это означает, что определение компонента FancyText будет оцениваться как на сервере, так и загружаться клиентом для рендеринга его клиентского использования.

Подробный анализ

Почему Copyright является серверным компонентом?

Поскольку 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'.

'use client' определяет границу между серверным и клиентским кодом в дереве зависимостей модулей.

В дереве зависимостей модулей мы видим, что App.js импортирует и вызывает Copyright из модуля Copyright.js. Поскольку Copyright.js не содержит директивы 'use client', использование компонента рендерится на сервере. App рендерится на сервере, так как это корневой компонент.

Клиентские компоненты могут рендерить серверные компоненты, потому что вы можете передавать JSX в качестве свойств. В этом случае InspirationGenerator получает Copyright как дочерние элементы. Однако модуль InspirationGenerator никогда не импортирует напрямую модуль Copyright и не вызывает компонент, всё это делает App. Фактически, компонент Copyright полностью выполняется до того, как InspirationGenerator начнёт рендеринг.

Вывод заключается в том, что родительско-дочерние отношения между компонентами не гарантируют одинаковую среду рендеринга.

Когда использовать 'use client'

С 'use client', вы можете определить, когда компоненты являются клиентскими компонентами. Поскольку серверные компоненты являются по умолчанию, здесь приведён краткий обзор преимуществ и ограничений серверных компонентов, чтобы определить, когда вам нужно пометить что-то как рендерящееся на клиенте.

Для простоты мы говорим о серверных компонентах, но те же принципы применяются ко всему коду вашего приложения, который выполняется на сервере.

Преимущества серверных компонентов

  • Серверные компоненты могут уменьшить количество кода, отправляемого и выполняемого клиентом. Только клиентские модули собираются и оцениваются клиентом.
  • Серверные компоненты извлекают выгоду из выполнения на сервере. Они могут получить доступ к локальной файловой системе и могут иметь низкую задержку при получении данных и сетевых запросах.

Ограничения серверных компонентов

  • Серверные компоненты не могут поддерживать взаимодействие, так как обработчики событий должны быть зарегистрированы и вызваны клиентом.
    • Например, обработчики событий, такие как onClick , могут быть определены только в клиентских компонентах.
  • Серверные компоненты не могут использовать большинство хуков.
    • Когда серверные компоненты рендерятся, их вывод представляет собой в основном список компонентов для рендеринга клиентом. Серверные компоненты не сохраняются в памяти после рендеринга и не могут иметь собственного состояния.

Сериализуемые типы данных, возвращаемые серверными компонентами

Как и в любом приложении React, родительские компоненты передают данные дочерним компонентам. Поскольку они рендерятся в разных средах, передача данных от серверного компонента к клиентскому компоненту требует дополнительных соображений.

Значения свойств, передаваемые от серверного компонента к клиентскому компоненту, должны быть сериализуемыми.

Сериализуемые свойства включают:

  • Примитивы
    • строка
    • число
    • bigint
    • логическое
    • неопределено
    • null
    • символ, только символы, зарегистрированные в глобальной регистрации Symbol через Symbol.for
  • Итерируемые объекты, содержащие сериализуемые значения
    • Строка
    • Массив
    • Карта
    • Множество
    • TypedArray и ArrayBuffer
  • Дата
  • Простые объекты: созданные с помощью инициализаторов объектов, с сериализуемыми свойствами
  • Функции, являющиеся Серверными функциями
  • Элементы клиентских или серверных компонентов (JSX)
  • Обещания

Следует отметить, что эти возможности не поддерживаются:

  • Функции, которые не экспортируются из помеченных как клиентские модулей или помеченные с помощью 'use server'
  • Классы
  • Объекты, которые являются экземплярами любого класса (кроме встроенных, упомянутых выше) или объекты с нулевым прототипом
  • Символы, не зарегистрированные глобально, например 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 , так и обработчиков событий для увеличения или уменьшения значения, этот компонент должен быть клиенским компонентом и потребует директиву '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', так как он не интерактивен и не использует состояние. Кроме того, 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 используется как серверный, так и клиентский компонент, в зависимости от того, где он импортирован и используется.

Но если HTML-вывод FancyText был велик по сравнению с исходным кодом (включая зависимости), то было бы эффективнее всегда делать его клиентским компонентом. Одним из таких случаев, когда может быть эффективнее сделать компонент клиентским, являются компоненты, возвращающие длинную строку SVG.

Использование клиентских API

Ваше приложение React может использовать специфичные для клиента API, такие как API браузера для работы с кэшем, обработкой аудио и видео, и аппаратным обеспечением устройства, среди других.

В этом примере компонент использует API DOM для обработки элемента 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 вы будете использовать сторонние библиотеки для обработки распространённых шаблонов пользовательского интерфейса или логики.

Эти библиотеки могут полагаться на хуки компонентов или клиентские API. Сторонние компоненты, использующие любые из следующих React API, должны выполняться на клиенте:

  • createContext
  • react и react-dom хуки, за исключением use и useId
  • forwardRef
  • memo
  • startTransition
  • Если они используют клиентские API, например, вставка DOM или просмотр нативных платформ

Если эти библиотеки были обновлены для совместимости с React серверными компонентами, то они уже будут включать 'use client' маркеры, позволяющие использовать их напрямую из серверных компонентов. Если библиотека не была обновлена или если компоненту нужны свойства, такие как обработчики событий, которые могут быть указаны только на клиенте, вам может потребоваться создать свой собственный файл клиентского компонента между сторонним клиентским компонентом и вашим серверным компонентом, где вы хотите его использовать.

© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://react.dev/reference/rsc/use-client

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API