resume 将预渲染的 React 树流式传输到一个 可读取的 Web 流。
const stream = await resume(reactNode, postponedState, options?)参考
🌐 Reference
resume(node, postponedState, options?)
调用 resume 将预渲染的 React 树作为 HTML 渲染到 可读 Web 流 中
🌐 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)
}参数
🌐 Parameters
reactNode:你调用的 React 节点prerender。例如,一个像<App />这样的 JSX 元素。它预计表示整个文档,因此App组件应渲染<html>标签。postponedState:从 prerender API 返回的不可见postpone对象,从你存储它的地方加载(例如 redis、文件或 S3)。- 可选
options:一个带有流选项的对象。
返回
🌐 Returns
resume 返回一个 Promise:
- 如果
resume成功生成了一个 shell,该 Promise 将会解析为一个可以被导入到 Writable Web Stream 的 Readable Web Stream。 - 如果 shell 中发生错误,Promise 将拒绝该错误。
返回的流有一个额外的属性:
🌐 The returned stream has an additional property:
allReady:一个在所有渲染完成时解决的 Promise。你可以在返回响应之前await stream.allReady用于爬虫和静态生成。如果你这样做,你将无法获得任何渐进式加载。流将包含最终的 HTML。
注意事项
🌐 Caveats
resume不接受bootstrapScripts、bootstrapScriptContent或bootstrapModules的选项。相反,你需要将这些选项传递给生成postponedState的prerender调用。你也可以手动将引导内容注入可写流。resume不接受identifierPrefix,因为前缀在prerender和resume中需要相同。- 由于无法向预渲染提供
nonce,只有在不向预渲染提供脚本的情况下,你才应该将nonce提供给resume。 resume会从根重新渲染,直到找到一个未完全预渲染的组件。只有完全预渲染的组件(组件及其子组件完成预渲染)会被完全跳过。
用法
🌐 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
Resuming 的行为类似于 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.