resume 将预渲染的 React 树流式传输到 可读网络流。

¥resume streams a pre-rendered React tree to a Readable Web Stream.

const stream = await resume(reactNode, postponedState, options?)

注意

此 API 依赖于 网络流。 对于 Node.js,请改用 resumeToNodeStream

¥This API depends on Web Streams. For Node.js, use resumeToNodeStream instead.


参考

¥Reference

resume(node, postponedState, options?)

调用 resume 恢复将预渲染的 React 树以 HTML 形式渲染到 可读网络流。

¥Call resume to resume rendering a pre-rendered React tree as HTML into a 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)
}

请参阅下面的更多示例。

¥See more examples below.

参数

¥Parameters

  • reactNode:调用 prerender 的 React 节点。例如,像 <App /> 这样的 JSX 元素。它应该代表整个文档,所以 App 组件应该渲染 <html> 标签。

    ¥reactNode: The React node you called prerender with. For example, a JSX element like <App />. It is expected to represent the entire document, so the App component should render the <html> tag.

  • postponedState:从 预渲染 API 返回的不透明 postpone 对象,从存储它的任何位置加载(例如,Redis、文件或 S3)。

    ¥postponedState: The opaque postpone object returned from a prerender API, loaded from wherever you stored it (e.g. redis, a file, or S3).

  • 可选 options:具有流选项的对象。

    ¥optional options: An object with streaming options.

返回

¥Returns

resume 返回一个 Promise:

¥resume returns a Promise:

返回的流有一个额外的属性:

¥The returned stream has an additional property:

  • allReady:一个 Promise,当所有渲染完成时解析。你可以在返回响应 用于爬虫和静态生成。 之前执行 await stream.allReady。如果这样做,你将无法获得任何渐进式加载。该流将包含最终的 HTML。

    ¥allReady: A Promise that resolves when all rendering is complete. You can await stream.allReady before returning a response for crawlers and static generation. If you do that, you won’t get any progressive loading. The stream will contain the final HTML.

注意事项

¥Caveats

  • resume 不接受 bootstrapScriptsbootstrapScriptContentbootstrapModules 的选项。相反,你需要将这些选项传递给生成 postponedStateprerender 调用。你还可以手动将引导内容注入可写流。

    ¥resume does not accept options for bootstrapScripts, bootstrapScriptContent, or bootstrapModules. Instead, you need to pass these options to the prerender call that generates the postponedState. You can also inject bootstrap content into the writable stream manually.

  • resume 不接受 identifierPrefix,因为前缀在 prerenderresume 中必须相同。

    ¥resume does not accept identifierPrefix since the prefix needs to be the same in both prerender and resume.

  • 由于无法将 nonce 提供给预渲染,因此如果你不提供脚本来提供预渲染,则应仅提供 nonceresume

    ¥Since nonce cannot be provided to prerender, you should only provide nonce to resume if you’re not providing scripts to prerender.

  • resume 从根节点重新渲染,直到找到未完全预渲染的组件。只有完全预渲染的组件(组件及其子组件已完成预渲染)才会被完全跳过。

    ¥resume re-renders from the root until it finds a component that was not fully pre-rendered. Only fully prerendered Components (the Component and its children finished prerendering) are skipped entirely.

用法

¥Usage

恢复预渲染

¥Resuming a prerender

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"));

延伸阅读

¥Further reading

恢复行为与 renderToReadableStream 相同。更多示例,请查看 renderToReadableStream 的使用部分prerender 的使用部分 包含如何使用 prerender 的具体示例。

¥Resuming behaves like renderToReadableStream. For more examples, check out the usage section of renderToReadableStream. The usage section of prerender includes examples of how to use prerender specifically.