prerender
は、Webストリームを使用して、Reactツリーを静的HTML文字列にレンダリングします。
const {prelude} = await prerender(reactNode, options?)
リファレンス
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です。SVGの場合は'http://www.w3.org/2000/svg'
、MathMLの場合は'http://www.w3.org/1998/Math/MathML'
を渡します。 - オプション
onError
: 回復可能か回復不可能かに関わらず、サーバーエラーが発生するたびに実行されるコールバック。デフォルトでは、console.error
を呼び出すだけです。クラッシュレポートをログに記録するためにオーバーライドする場合は、必ずconsole.error
も呼び出すようにしてください。また、シェルが出力される前にステータスコードを調整するためにも使用できます。 - オプション
progressiveChunkSize
: チャンクのバイト数。デフォルトのヒューリスティックの詳細はこちら。 - オプション
signal
: アボートシグナル。 サーバーレンダリングを中止し、残りをクライアントでレンダリングできます。
- **オプション**
戻り値
prerender
はPromiseを返します。
- レンダリングが成功した場合、Promiseは次のオブジェクトを解決します。
prelude
: HTMLのWebストリーム。このストリームを使用して、チャンク単位でレスポンスを送信したり、ストリーム全体を文字列に読み込んだりできます。
- レンダリングが失敗した場合、Promiseは拒否されます。フォールバックシェルを出力するには、これを使用します。
使用方法 ...
Reactツリーを静的HTMLのストリームにレンダリングする ...
prerender
を呼び出して、Reactツリーを静的HTMLにレンダリングし、Readable 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はDOCTYPEとブートストラップ<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にイベントリスナーがアタッチされ、インタラクティブになります。
詳細
最終的なアセット URL(JavaScript や CSS ファイルなど)は、ビルド後にハッシュ化されることがよくあります。たとえば、`styles.css` の代わりに `styles.123456.css` のようなファイル名になります。静的アセットのファイル名をハッシュ化することで、同じアセットの異なるビルドごとに異なるファイル名が保証されます。これは、静的アセットの長期キャッシュを安全に有効にできるため便利です。特定の名前のファイルの内容が変更されることはありません。
ただし、ビルド後までアセット URL がわからない場合、ソースコードにそれらを配置する方法がありません。たとえば、前述のように JSX に `"/styles.css"` をハードコーディングしても機能しません。ソースコードからそれらを分離するために、ルートコンポーネントは小道具として渡されたマップから実際のファイル名を読み取ることができます。
export default function App({ assetMap }) {
return (
<html>
<head>
<title>My app</title>
<link rel="stylesheet" href={assetMap['styles.css']}></link>
</head>
...
</html>
);
}
サーバー上で、`
// 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' },
});
}
サーバーが `
// 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` オプションは、クライアント側でグローバル変数 `window.assetMap` を設定する追加のインライン `