prerender 使用 Web 串流 將 React 樹狀結構渲染成靜態 HTML 字串。

const {prelude} = await prerender(reactNode, options?)

注意事項

此 API 仰賴 Web 串流。對於 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:您想要渲染成 HTML 的 React 節點。例如,像 <App /> 這樣的 JSX 節點。它預期代表整個文件,因此 App 元件應該渲染 <html> 標籤。

  • 選用 options:具有靜態產生選項的物件。

    • 選用 bootstrapScriptContent:如果指定,此字串將會被放置在一個內聯的 <script> 標籤中。
    • 選用 bootstrapScripts:要在頁面上輸出的 <script> 標籤字串 URL 陣列。使用它來包含呼叫 hydrateRoot<script>。如果您根本不想在客戶端上運行 React,請省略它。
    • 選用 bootstrapModules:類似於 bootstrapScripts,但會輸出 <script type="module">
    • 選用 identifierPrefix:React 用於由 useId 產生的 ID 的字串前綴。可用於避免在同一個頁面上使用多個根目錄時發生衝突。必須與傳遞給 hydrateRoot 的前綴相同。
    • 選用 namespaceURI:具有串流根 命名空間 URI 的字串。預設為一般 HTML。傳遞 'http://www.w3.org/2000/svg' 表示 SVG,或傳遞 'http://www.w3.org/1998/Math/MathML' 表示 MathML。
    • 選用 onError:無論是可恢復的還是不可恢復的,只要發生伺服器錯誤就會觸發的回呼函式。預設情況下,這只會呼叫 console.error。如果您將其覆蓋以記錄崩潰報告,請確保您仍然呼叫 console.error。您也可以使用它在發出 shell 之前調整狀態碼
    • 選用 progressiveChunkSize:區塊中的位元組數。閱讀更多關於預設啟發式方法的資訊。
    • 選用 signal:一個 中止訊號,可讓您中止伺服器渲染並在客戶端上渲染其餘部分。

回傳

prerender 回傳一個 Promise

  • 如果渲染成功,Promise 將解析為一個包含以下內容的物件:
    • prelude:HTML 的 Web 串流。您可以使用此串流分塊發送響應,也可以將整個串流讀取到字串中。
  • 如果渲染失敗,Promise 將被拒絕。使用它來輸出備用 shell。

注意事項

我應該什麼時候使用 prerender

靜態 prerender API 用於靜態伺服器端生成 (SSG)。與 renderToString 不同,prerender 會等待所有資料載入後再解析。這使得它適用於生成完整頁面的靜態 HTML,包括需要使用 Suspense 提取的資料。要隨著載入串流內容,請使用串流伺服器端渲染 (SSR) API,例如 renderToReadableStream


用法

將 React 樹狀結構渲染為靜態 HTML 串流

呼叫 prerender 將您的 React 樹狀結構渲染為靜態 HTML 到 可讀取的 Web 串流:

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' },
});
}

除了 根組件 之外,您還需要提供 引導 <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 將把 文件類型 和您的 引導 <script> 標籤 注入到產生的 HTML 串流中

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

在客戶端上,您的引導指令碼應透過呼叫 hydrateRoot 來水合整個 文件

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

hydrateRoot(document, <App />);

這會將事件監聽器附加到靜態伺服器生成的 HTML 並使其具有互動性。

深入探討

從建置輸出讀取 CSS 和 JS 資源路徑

最終的資源網址(例如 JavaScript 和 CSS 檔案)通常在建置後會加上雜湊值。例如,`styles.css` 可能會變成 `styles.123456.css`。雜湊靜態資源檔名可確保相同資源的每個不同建置版本都具有不同的檔名。這很有用,因為它允許您安全地啟用靜態資源的長期快取:具有特定名稱的檔案內容永遠不會更改。

但是,如果您在建置完成之前不知道資源網址,就無法將它們放入原始碼中。例如,像前面那樣將 `"/styles.css"` 硬編碼到 JSX 中將無法正常運作。為了將它們排除在原始碼之外,您的根組件可以從作為 prop 傳遞的地圖中讀取實際檔名。

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

在伺服器上,渲染 `` 並傳遞您的 `assetMap` 以及資源網址。

// 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' },
});
}

由於您的伺服器現在正在渲染 ``,因此您也需要在客戶端使用 `assetMap` 渲染它,以避免水合錯誤(hydration errors)。您可以像這樣序列化 `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` 選項會新增一個額外的內聯 `