Spec-Zone.ru › React

prerender

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

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

Примечание

Этот API зависит от Web Streams. Для Node.js используйте prerenderToNodeStream вместо этого.


Справочник

prerender(reactNode, options?)

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

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

async function handler(request) {
  const {prelude} = await prerender(<App />, {
    bootstrapScripts: ['/main.js']
  });
  return new Response(prelude, {
    headers: { 'content-type': 'text/html' },
  });
}

На клиенте вызовите 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 использует для ID, сгенерированных с помощью 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: сигнал прерывания, который позволяет прервать рендеринг на сервере и выполнить рендеринг остальной части на клиенте.

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

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

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

Примечание

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

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


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

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

Вызовите prerender для рендеринга вашего дерева React в статический HTML в Readable Web Stream::

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

async function handler(request) {
  const {prelude} = await prerender(<App />, {
    bootstrapScripts: ['/main.js']
  });
  return new Response(prelude, {
    headers: { 'content-type': 'text/html' },
  });
}

Вместе с компонентом корня необходимо предоставить список путей загрузки bootstrap <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 и ваши bootstrap <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'
};

async function handler(request) {
  const {prelude} = await prerender(<App assetMap={assetMap} />, {
    bootstrapScripts: [assetMap['/main.js']]
  });
  return new Response(prelude, {
    headers: { 'content-type': 'text/html' },
  });
}

Поскольку ваш сервер теперь рендерит <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'
};

async function handler(request) {
  const {prelude} = await prerender(<App assetMap={assetMap} />, {
    // Careful: It's safe to stringify() this because this data isn't user-generated.
    bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`,
    bootstrapScripts: [assetMap['/main.js']],
  });
  return new Response(prelude, {
    headers: { 'content-type': 'text/html' },
  });
}

В приведенном выше примере опция 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

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

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

async function renderToString() {
  const {prelude} = await prerender(<App />, {
    bootstrapScripts: ['/main.js']
  });
  
  const reader = stream.getReader();
  let content = '';
  while (true) {
    const {done, value} = await reader.read();
    if (done) {
      return content;
    }
    content += Buffer.from(value).toString('utf8');
  }
}

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


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

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

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

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

Примечание

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

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

Suspense не обнаруживает загрузку данных внутри эффекта или обработчика событий.

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

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


Отладка

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

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

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

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

Spec-Zone.ru

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