Spec-Zone.ru › React

prerenderToNodeStream

prerender преобразует дерево React в статическую строку HTML с использованием потока Node.js Stream..

const {prelude} = await prerenderToNodeStream(reactNode, options?)
  • Справочная информация
    • prerenderToNodeStream(reactNode, options?)
  • Использование
    • Рендеринг дерева React в поток статического HTML
    • Рендеринг дерева React в строку статического HTML
    • Ожидание загрузки всех данных
  • Отладка
    • Поток не запускается до полной отрисовки приложения

Примечание

Этот API специфичен для Node.js. Среды с Web Streams, такие как Deno и современные среды выполнения на кромке, должны использовать prerender вместо этого.


Справочная информация

prerenderToNodeStream(reactNode, options?)

Вызовите prerenderToNodeStream для рендеринга вашего приложения в статический HTML.

import { prerenderToNodeStream } from 'react-dom/static';

// The route handler syntax depends on your backend framework
app.use('/', async (request, response) => {
  const { prelude } = await prerenderToNodeStream(<App />, {
    bootstrapScripts: ['/main.js'],
  });

  response.setHeader('Content-Type', 'text/plain');
  prelude.pipe(response);
});

На стороне клиента вызовите hydrateRoot, чтобы сделать сгенерированный сервером HTML интерактивным.

См. дополнительные примеры ниже.

Параметры

  • reactNode: Узел React, который вы хотите отобразить в HTML. Например, узел JSX, такой как <App />. Ожидается, что он будет представлять весь документ, поэтому компонент App должен отображать тег <html>.

  • необязательно options: Объект со ставками статического генерирования.

    • необязательно bootstrapScriptContent: Если указано, эта строка будет помещена в тег inline <script>.
    • необязательно bootstrapScripts: Массив строк URL для тегов <script> для вывода на странице. Используйте это для включения <script>, который вызывает hydrateRoot. Опустите его, если вы не хотите запускать React на клиенте вообще.
    • необязательно bootstrapModules: Как bootstrapScripts, но выводит <script type="module"> вместо этого.
    • необязательно identifierPrefix: Префикс строки, который React использует для идентификаторов, сгенерированных useId. Полезно для предотвращения конфликтов при использовании нескольких корней на одной странице. Должен совпадать с префиксом, переданным в hydrateRoot.
    • необязательно namespaceURI: Строка с URI пространства имен корня пространства имен для потока. По умолчанию используется обычный HTML. Передайте 'http://www.w3.org/2000/svg' для SVG или 'http://www.w3.org/1998/Math/MathML' для MathML.
    • необязательно onError: Обратный вызов, который срабатывает всякий раз, когда возникает ошибка сервера, независимо от того, восстанавливаемая она или нет. По умолчанию это только вызывает console.error. Если вы переопределите его, чтобы записывать отчеты о сбоях, убедитесь, что вы все еще вызываете console.error. Вы также можете использовать его, чтобы изменить код состояния перед выводом оболочки.
    • необязательно progressiveChunkSize: Количество байтов в фрагменте. Подробнее о базовом алгоритме.
    • необязательно signal: сигнал отмены, который позволяет отменить рендеринг на сервере и отобразить остальную часть на клиенте.

Возвращаемое значение

prerenderToNodeStream возвращает Promise:

  • Если рендеринг выполнен успешно, Promise разрешит объект, содержащий:
    • prelude: поток Node.js HTML. Вы можете использовать этот поток для отправки ответа частями или можете прочитать весь поток в строку.
  • Если рендеринг завершится неудачей, Promise будет отклонен. Используйте это для вывода резервной оболочки.

Примечание

Когда следует использовать prerenderToNodeStream?

API статического prerenderToNodeStream используется для статического генерации на стороне сервера (SSG). В отличие от renderToString, prerenderToNodeStream ожидает загрузки всех данных перед разрешением. Это делает его подходящим для создания статического HTML для полной страницы, включая данные, которые нужно извлечь с помощью Suspense. Для потоковой передачи контента по мере загрузки используйте API потокового рендеринга на стороне сервера (SSR), такой как renderToReadableStream.


Использование

Рендеринг дерева React в поток статического HTML

Вызовите prerenderToNodeStream для рендеринга вашего дерева React в статический HTML в Node.js Stream.:

import { prerenderToNodeStream } from 'react-dom/static';

// The route handler syntax depends on your backend framework
app.use('/', async (request, response) => {
  const { prelude } = await prerenderToNodeStream(<App />, {
    bootstrapScripts: ['/main.js'],
  });
  
  response.setHeader('Content-Type', 'text/plain');
  prelude.pipe(response);
});

Вместе с компонентом корня вам необходимо предоставить список путей загрузки <script>. Ваш компонент корня должен возвращать весь документ, включая тег корневого <html>.

Например, он может выглядеть так:

export default function App() {
  return (
    <html>
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="stylesheet" href="/styles.css"></link>
        <title>My app</title>
      </head>
      <body>
        <Router />
      </body>
    </html>
  );
}

React внедрит DOCTYPE и ваши теги загрузки <script> в результирующий поток HTML:

<!DOCTYPE html>
<html>
  <!-- ... HTML from your components ... -->
</html>
<script src="/main.js" async=""></script>

На клиенте ваш скрипт загрузки должен гидратировать весь document с вызовом hydrateRoot:

import { hydrateRoot } from 'react-dom/client';
import App from './App.js';

hydrateRoot(document, <App />);

Это позволит прикрепить обработчики событий к статическому сгенерированному сервером HTML и сделать его интерактивным.

Подробное описание

Чтение путей к файлам CSS и JS из выходных данных сборки

Окончательные URL-адреса ресурсов (например, файлы JavaScript и CSS) часто хэшируются после сборки. Например, вместо styles.css вы можете получить styles.123456.css. Хэширование имен файлов статических ресурсов гарантирует, что каждая отдельная сборка одного и того же ресурса будет иметь другое имя файла. Это полезно, поскольку позволяет безопасно включить кеширование статических ресурсов на длительный срок: файл с определенным именем никогда не будет содержать другой контент.

Однако, если вы не знаете URL-адреса ресурсов до сборки, у вас нет способа поместить их в исходный код. Например, жесткое кодирование "/styles.css" в JSX, как раньше, не сработает. Чтобы сохранить их вне исходного кода, ваш компонент корня может считывать реальные имена файлов из карты, переданной в качестве свойства:

export default function App({ assetMap }) {
  return (
    <html>
      <head>
        <title>My app</title>
        <link rel="stylesheet" href={assetMap['styles.css']}></link>
      </head>
      ...
    </html>
  );
}

На сервере отобразите <App assetMap={assetMap} /> и передайте вашу assetMap с URL-адресами ресурсов:

// You'd need to get this JSON from your build tooling, e.g. read it from the build output.
const assetMap = {
  'styles.css': '/styles.123456.css',
  'main.js': '/main.123456.js'
};

app.use('/', async (request, response) => {
  const { prelude } = await prerenderToNodeStream(<App />, {
    bootstrapScripts: [assetMap['/main.js']]
  });

  response.setHeader('Content-Type', 'text/html');
  prelude.pipe(response);
});

Поскольку ваш сервер теперь отображает <App assetMap={assetMap} />, вам необходимо отобразить его с assetMap на клиенте тоже, чтобы избежать ошибок гидратации. Вы можете сериализовать и передать assetMap клиенту так:

// You'd need to get this JSON from your build tooling.
const assetMap = {
  'styles.css': '/styles.123456.css',
  'main.js': '/main.123456.js'
};

app.use('/', async (request, response) => {
  const { prelude } = await prerenderToNodeStream(<App />, {
    // Careful: It's safe to stringify() this because this data isn't user-generated.
    bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`,
    bootstrapScripts: [assetMap['/main.js']],
  });

  response.setHeader('Content-Type', 'text/html');
  prelude.pipe(response);
});

В примере выше, опция bootstrapScriptContent добавляет дополнительный inline тег <script> который устанавливает глобальную переменную window.assetMap на клиенте. Это позволяет коду клиента читать те же assetMap:

import { hydrateRoot } from 'react-dom/client';
import App from './App.js';

hydrateRoot(document, <App assetMap={window.assetMap} />);

Как клиент, так и сервер отображают App с одинаковым свойством assetMap, поэтому ошибок гидратации нет.


Рендеринг дерева React в строку статического HTML

Вызовите prerenderToNodeStream для рендеринга вашего приложения в статическую строку HTML:

import { prerenderToNodeStream } from 'react-dom/static';

async function renderToString() {
  const {prelude} = await prerenderToNodeStream(<App />, {
    bootstrapScripts: ['/main.js']
  });
  
  return new Promise((resolve, reject) => {
    let data = '';
    prelude.on('data', chunk => {
      data += chunk;
    });
    prelude.on('end', () => resolve(data));
    prelude.on('error', reject);
  });
}

Это создаст начальный неинтерактивный HTML-выход ваших компонентов React. На клиенте вам необходимо вызвать hydrateRoot, чтобы гидратировать этот сгенерированный сервером HTML и сделать его интерактивным.


Ожидание загрузки всех данных

prerenderToNodeStream ожидает загрузки всех данных перед завершением генерации статического HTML и разрешением. Например, рассмотрим страницу профиля, на которой отображается обложка, боковая панель с друзьями и фотографиями и список постов:

function ProfilePage() {
  return (
    <ProfileLayout>
      <ProfileCover />
      <Sidebar>
        <Friends />
        <Photos />
      </Sidebar>
      <Suspense fallback={<PostsGlimmer />}>
        <Posts />
      </Suspense>
    </ProfileLayout>
  );
}

Представьте, что <Posts /> нужно загрузить некоторые данные, что занимает некоторое время. В идеале вы хотели бы подождать, пока посты не закончат загружаться, чтобы они были включены в HTML. Для этого вы можете использовать Suspense, чтобы приостановить работу с данными, и prerenderToNodeStream будет ждать завершения приостановленного контента перед разрешением статического HTML.

Примечание

Только данные, поддерживающие Suspense, будут активировать компонент Suspense. Они включают:

  • Загрузка данных с помощью поддерживающих Suspense фреймворков, таких как Relay и Next.js
  • Ленивая загрузка кода компонента с помощью lazy
  • Чтение значения Promise с помощью use

Suspense не обнаруживает, когда данные загружаются внутри Effect или обработчика событий.

Точный способ загрузки данных в компоненте Posts зависит от вашего фреймворка. Если вы используете фреймворк, поддерживающий Suspense, вы найдете подробности в документации по загрузке данных.

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


Отладка

Поток не запускается до полной отрисовки приложения

Ответ prerenderToNodeStream ожидает завершения рендеринга всего приложения, включая ожидание разрешения всех границ неопределенности, перед разрешением. Он предназначен для статического генерирования сайтов (SSG) заранее и не поддерживает потоковую передачу большего количества контента по мере загрузки.

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

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

Spec-Zone.ru

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