resume
resume передает предварительно отрендеренное дерево React потоком в Readable Web Stream.
const stream = await resume(reactNode, postponedState, options?)
Примечание
Этот API зависит от Web Streams. Для Node.js используйте вместо этого resumeToNodeStream.
Справочник
resume(node, postponedState, options?)
Вызовите resume, чтобы возобновить рендеринг предварительно отрендеренного дерева React в виде HTML в Readable Web Stream.
import { resume } from 'react-dom/server';
import {getPostponedState} from './storage';
async function handler(request, writable) {
const postponed = await getPostponedState(request);
const resumeStream = await resume(<App />, postponed);
return resumeStream.pipeTo(writable)
} Параметры
-
reactNode: Узел React, с которым вы вызывалиprerender. Например, JSX-элемент вида<App />. Ожидается, что он представляет весь документ, поэтому компонентAppдолжен рендерить тег<html>. -
postponedState: Непрозрачный объектpostpone, возвращенный из API пререндеринга, загруженный из места его хранения (например, redis, файл или S3). -
необязательно
options: Объект с параметрами потоковой передачи.-
необязательно
nonce: Строкаnonceдля разрешения скриптов согласноscript-srcContent-Security-Policy. -
необязательно
signal: Сигнал отмены, который позволяет вам прервать серверный рендеринг и отрендерить остальное на клиенте. -
необязательно
onError: Функция обратного вызова, которая срабатывает при любой ошибке сервера, как восстановимой, так и нет. По умолчанию она вызывает толькоconsole.error. Если вы переопределяете её, чтобы логировать отчеты о сбоях, убедитесь, что вы по-прежнему вызываетеconsole.error.
-
необязательно
Возвращаемое значение
resume возвращает Promise:
- Если
resumeуспешно создал оболочку (shell), этот Promise разрешится в Readable Web Stream, который можно перенаправить в Writable Web Stream. - Если в оболочке произойдет ошибка, Promise будет отклонен с этой ошибкой.
Возвращаемый поток имеет дополнительное свойство:
-
allReady: Promise, который разрешается по завершении всего рендеринга. Вы можете выполнитьawait stream.allReadyперед возвратом ответа для поисковых роботов и статической генерации. В этом случае вы не получите прогрессивной загрузки. Поток будет содержать окончательный HTML.
Важные особенности
-
resumeне принимает параметры дляbootstrapScripts,bootstrapScriptContentилиbootstrapModules. Вместо этого вам необходимо передать эти параметры в вызовprerender, который генерируетpostponedState. Вы также можете внедрить содержимое bootstrap в записываемый поток вручную. -
resumeне принимаетidentifierPrefix, поскольку префикс должен быть одинаковым как вprerender, так и вresume. - Поскольку
nonceнельзя передать в prerender, следует указыватьnonceвresumeтолько в том случае, если вы не передаете скрипты в prerender. -
resumeвыполняет повторный рендеринг от корня до тех пор, пока не найдет компонент, который не был полностью предварительно отрендерен. Полностью пропускаются только те компоненты, пререндеринг которых был завершен целиком (сам компонент и его дочерние элементы).
Использование
Возобновление пререндеринга
import { flushReadableStreamToFrame, getUser, Postponed, sleep, } from "./demo-helpers"; import { StrictMode, Suspense, use, useEffect } from "react"; import { prerender } from "react-dom/static"; import { resume } from "react-dom/server"; import { hydrateRoot } from "react-dom/client"; function Header() { return <header>Me and my descendants can be prerendered</header>; } const { promise: cookies, resolve: resolveCookies } = Promise.withResolvers(); function Main() { const { sessionID } = use(cookies); const user = getUser(sessionID); useEffect(() => { console.log("reached interactivity!"); }, []); return ( <main> Hello, {user.name}! <button onClick={() => console.log("hydrated!")}> Clicking me requires hydration. </button> </main> ); } function Shell({ children }) { // In a real app, this is where you would put your html and body. // We're just using tags here we can include in an existing body for demonstration purposes return ( <html> <body>{children}</body> </html> ); } function App() { return ( <Shell> <Suspense fallback="loading header"> <Header /> </Suspense> <Suspense fallback="loading main"> <Main /> </Suspense> </Shell> ); } async function main(frame) { // Layer 1 const controller = new AbortController(); const prerenderedApp = prerender(<App />, { signal: controller.signal, onError(error) { if (error instanceof Postponed) { } else { console.error(error); } }, }); // We're immediately aborting in a macrotask. // Any data fetching that's not available synchronously, or in a microtask, will not have finished. setTimeout(() => { controller.abort(new Postponed()); }); const { prelude, postponed } = await prerenderedApp; await flushReadableStreamToFrame(prelude, frame); // Layer 2 // Just waiting here for demonstration purposes. // In a real app, the prelude and postponed state would've been serialized in Layer 1 and Layer would deserialize them. // The prelude content could be flushed immediated as plain HTML while // React is continuing to render from where the prerender left off. await sleep(2000); // You would get the cookies from the incoming HTTP request resolveCookies({ sessionID: "abc" }); const stream = await resume(<App />, postponed); await flushReadableStreamToFrame(stream, frame); // Layer 3 // Just waiting here for demonstration purposes. await sleep(2000); hydrateRoot(frame.contentWindow.document, <App />); } main(document.getElementById("container"));
Дополнительные материалы
Возобновление работает аналогично renderToReadableStream. Другие примеры можно найти в разделе использования renderToReadableStream. Раздел использования prerender содержит примеры того, как использовать конкретно prerender.
© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://react.dev/reference/react-dom/server/resume