Spec-Zone.ru › React

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

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

Компоненты сервера предназначены для использования в React Server Components.

Компоненты сервера — это новый тип компонентов, которые рендерятся заранее, до объединения, в среде, отличной от приложения клиента или сервера SSR.

Эта отдельная среда — это «сервер» в React Server Components. Компоненты сервера могут выполняться один раз во время сборки на сервере CI или они могут выполняться для каждого запроса с помощью веб-сервера.

  • Компоненты сервера без сервера
  • Компоненты сервера с сервером
  • Добавление интерактивности в компоненты сервера
  • Асинхронные компоненты с компонентами сервера

Примечание

Как создать поддержку компонентов сервера?

Хотя компоненты сервера React в React 19 стабильны и не будут вызывать ошибки между основными версиями, основанные на них API, используемые для реализации сборщика или фреймворка компонентов сервера React, не следуют принципу semver и могут вызывать ошибки между младшими версиями в React 19.x.

Чтобы поддерживать компоненты сервера React как сборщик или фреймворк, мы рекомендуем зафиксировать конкретную версию React или использовать релиз Canary. Мы продолжим сотрудничество со сборщиками и фреймворками, чтобы стабилизировать API, используемые для реализации компонентов сервера React в будущем.

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

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

Без компонентов сервера обычно статические данные извлекаются на клиенте с помощью эффекта:

// bundle.js
import marked from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)

function Page({page}) {
  const [content, setContent] = useState('');
  // NOTE: loads *after* first page render.
  useEffect(() => {
    fetch(`/api/content/${page}`).then((data) => {
      setContent(data.content);
    });
  }, [page]);
  
  return <div>{sanitizeHtml(marked(content))}</div>;
}
// api.js
app.get(`/api/content/:page`, async (req, res) => {
  const page = req.params.page;
  const content = await file.readFile(`${page}.md`);
  res.send({content});
});

Этот шаблон означает, что пользователям необходимо загрузить и разобрать дополнительные 75К (сжатые) библиотек и подождать второго запроса для получения данных после загрузки страницы, только для того, чтобы отобразить статический контент, который не будет меняться в течение всего срока действия страницы.

С компонентами сервера вы можете отобразить эти компоненты один раз во время сборки:

import marked from 'marked'; // Not included in bundle
import sanitizeHtml from 'sanitize-html'; // Not included in bundle

async function Page({page}) {
  // NOTE: loads *during* render, when the app is built.
  const content = await file.readFile(`${page}.md`);
  
  return <div>{sanitizeHtml(marked(content))}</div>;
}

Полученный результат может быть отображён на сервере (SSR) в HTML и загружен на CDN. При загрузке приложения клиент не увидит исходный Page компонент или дорогостоящие библиотеки для отображения разметки Markdown. Клиент увидит только отрендеренный результат:

<div><!-- html for markdown --></div>

Это означает, что контент виден во время первой загрузки страницы, и сборка не включает дорогостоящие библиотеки, необходимые для отображения статического контента.

Примечание

Вы можете заметить, что компонент сервера выше — это асинхронная функция:

async function Page({page}) {
  //...
}

Асинхронные компоненты — это новая функция компонентов сервера, которая позволяет вам await в рендере.

См. Асинхронные компоненты с компонентами сервера ниже.

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

Компоненты сервера также могут выполняться на веб-сервере во время запроса страницы, позволяя вам получить доступ к вашему слою данных без необходимости создания API. Они отображаются до объединения вашего приложения и могут передавать данные и JSX в качестве свойств клиентским компонентам.

Без компонентов сервера обычно динамические данные извлекаются на клиенте в эффекте:

// bundle.js
function Note({id}) {
  const [note, setNote] = useState('');
  // NOTE: loads *after* first render.
  useEffect(() => {
    fetch(`/api/notes/${id}`).then(data => {
      setNote(data.note);
    });
  }, [id]);
  
  return (
    <div>
      <Author id={note.authorId} />
      <p>{note}</p>
    </div>
  );
}

function Author({id}) {
  const [author, setAuthor] = useState('');
  // NOTE: loads *after* Note renders.
  // Causing an expensive client-server waterfall.
  useEffect(() => {
    fetch(`/api/authors/${id}`).then(data => {
      setAuthor(data.author);
    });
  }, [id]);

  return <span>By: {author.name}</span>;
}
// api
import db from './database';

app.get(`/api/notes/:id`, async (req, res) => {
  const note = await db.notes.get(id);
  res.send({note});
});

app.get(`/api/authors/:id`, async (req, res) => {
  const author = await db.authors.get(id);
  res.send({author});
});

С компонентами сервера вы можете прочитать данные и отобразить их в компоненте:

import db from './database';

async function Note({id}) {
  // NOTE: loads *during* render.
  const note = await db.notes.get(id);
  return (
    <div>
      <Author id={note.authorId} />
      <p>{note}</p>
    </div>
  );
}

async function Author({id}) {
  // NOTE: loads *after* Note,
  // but is fast if data is co-located.
  const author = await db.authors.get(id);
  return <span>By: {author.name}</span>;
}

Затем сборщик объединяет данные, отрендеренные компоненты сервера и динамические клиентские компоненты в сборку. При необходимости эта сборка может быть отображена на сервере (SSR) для создания начального HTML-кода страницы. При загрузке страницы браузер не видит исходные Note и Author компоненты; только рендерный результат отправляется клиенту:

<div>
  <span>By: The React Team</span>
  <p>React 19 is...</p>
</div>

Компоненты сервера могут быть сделаны динамичными путём повторного извлечения их с сервера, где они могут получить доступ к данным и снова отобразиться. Эта новая архитектура приложения объединяет простую «модель запроса/ответа» серверно-ориентированных многостраничных приложений с плавной интерактивностью клиентно-ориентированных одностраничных приложений, предоставляя вам лучшее из обоих миров.

Добавление интерактивности в компоненты сервера

Компоненты сервера не отправляются в браузер, поэтому они не могут использовать интерактивные API, такие как useState. Чтобы добавить интерактивность в компоненты сервера, вы можете комбинировать их с клиентскими компонентами, используя директиву "use client".

Примечание

Нет директивы для компонентов сервера.

Распространённое заблуждение заключается в том, что компоненты сервера обозначаются "use server", но директивы для компонентов сервера нет. Директива "use server" используется для серверных функций.

Дополнительную информацию см. в документации по Директивам.

В следующем примере Notes компонент сервера импортирует Expandable клиентский компонент, который использует состояние для переключения своего expanded состояния:

// Server Component
import Expandable from './Expandable';

async function Notes() {
  const notes = await db.notes.getAll();
  return (
    <div>
      {notes.map(note => (
        <Expandable key={note.id}>
          <p note={note} />
        </Expandable>
      ))}
    </div>
  )
}
// Client Component
"use client"

export default function Expandable({children}) {
  const [expanded, setExpanded] = useState(false);
  return (
    <div>
      <button
        onClick={() => setExpanded(!expanded)}
      >
        Toggle
      </button>
      {expanded && children}
    </div>
  )
}

Это работает, сначала отобразив Notes как компонент сервера, а затем указав сборщику создать сборку для клиентского компонента Expandable. В браузере клиентские компоненты увидят вывод компонентов сервера, переданных в качестве свойств:

<head>
  <!-- the bundle for Client Components -->
  <script src="bundle.js" />
</head>
<body>
  <div>
    <Expandable key={1}>
      <p>this is the first note</p>
    </Expandable>
    <Expandable key={2}>
      <p>this is the second note</p>
    </Expandable>
    <!--...-->
  </div> 
</body>

Асинхронные компоненты с компонентами сервера

Компоненты сервера представляют новый способ написания компонентов с использованием async/await. Когда вы await в асинхронном компоненте, React приостановит выполнение и дождётся разрешения обещания, прежде чем продолжить рендеринг. Это работает на границах сервера/клиента с поддержкой потоковой передачи для Suspense.

Вы даже можете создать обещание на сервере и дождаться его на клиенте:

// Server Component
import db from './database';

async function Page({id}) {
  // Will suspend the Server Component.
  const note = await db.notes.get(id);
  
  // NOTE: not awaited, will start here and await on the client. 
  const commentsPromise = db.comments.get(note.id);
  return (
    <div>
      {note}
      <Suspense fallback={<p>Loading Comments...</p>}>
        <Comments commentsPromise={commentsPromise} />
      </Suspense>
    </div>
  );
}
// Client Component
"use client";
import {use} from 'react';

function Comments({commentsPromise}) {
  // NOTE: this will resume the promise from the server.
  // It will suspend until the data is available.
  const comments = use(commentsPromise);
  return comments.map(commment => <p>{comment}</p>);
}

Контент note важен для отображения страницы, поэтому мы await его на сервере. Комментарии расположены под складкой и имеют меньший приоритет, поэтому мы запускаем обещание на сервере и ждём его на клиенте с помощью use API. Это приостановит выполнение на клиенте, не блокируя отображение note контента.

Поскольку асинхронные компоненты не поддерживаются на клиенте, мы ждём обещание с use.

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

Spec-Zone.ru

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