React SSRの内部動作
renderToString/renderToPipeableStreamの流れと、クライアントがどのようにhydrationを引き継ぐかを追います。
「React SSRの内部動作」はCoddyKit上の無料React Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはReact Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 React Academyコースには全4レッスンが含まれています。
SSRが解決する問題
クライアントサイドReactでは、レンダリングにJavaScriptが必要です。JavaScriptの読み込みと実行が完了するまで、ユーザーには空白のページが表示されます。SSRはサーバー上でHTMLを事前にレンダリングするため、ユーザーはすぐにコンテンツを確認でき、体感パフォーマンスとSEOが向上します。
renderToString
元来のSSR APIです。renderToString(<App />)は、サーバー上でコンポーネントツリーを同期的にHTML文字列へレンダリングします。
import { renderToString } from 'react-dom/server';
import App from './App';
// In an Express handler:
app.get('*', (req, res) => {
const html = renderToString(<App />);
res.send(`
<!DOCTYPE html>
<html><body>
<div id="root">${html}</div>
<script src="/bundle.js"></script>
</body></html>
`);
});クライアントでのハイドレーション
HTMLが届くと、ブラウザーはHTMLを解析してUIを描画します。その後、ReactのhydrateRoot()がイベントリスナーを接続し、静的なHTMLを操作可能にします。これがハイドレーションです。
// client/index.tsx
import { hydrateRoot } from 'react-dom/client';
import App from './App';
hydrateRoot(document.getElementById('root'), <App />);ハイドレーション中に起きること
Reactは、サーバーでレンダリングされたDOMと仮想DOMを同時にたどります。一致していれば、DOMに変更を加えずにリスナーを接続します。異なる場合は、ハイドレーションの警告をログに出力し、DOMを修正します。
renderToPipeableStream
React 18のストリーミングAPIは、データが利用可能になるとHTMLをチャンク単位で送信します。これにより、サーバーが処理を続けている間もブラウザーはコンテンツを段階的に描画できます。
import { renderToPipeableStream } from 'react-dom/server';
app.get('*', (req, res) => {
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/bundle.js'],
onShellReady() {
res.setHeader('content-type', 'text/html');
pipe(res); // starts streaming
},
});
});Suspenseとストリーミング
ストリーミングSSRでは、Suspense境界がまずフォールバックをストリーミングし、データの解決後にコンテンツへ置き換えます。これにより、最初のHTMLの送信を妨げずに処理できます。
// The shell (nav, layout) streams immediately;
// <UserProfile> suspends and streams when data resolves
function App() {
return (
<Layout>
<Suspense fallback={<Skeleton />}>
<UserProfile /> {/* streams after data loads */}
</Suspense>
</Layout>
);
}データのシリアライズ
サーバーで取得したデータは、クライアントが再取得せずにハイドレーション中に読み取れるよう、シリアライズしてHTMLに埋め込む必要があります。
// Server:
const data = await fetchData();
const html = renderToString(<App data={data} />);
const serialized = JSON.stringify(data).replace(/</g, '\\u003c');
// Inline data in HTML:
`<script>window.__INITIAL_DATA__ = ${serialized};</script>`
// Client hydration reads:
const data = window.__INITIAL_DATA__;環境の違い
サーバーコードはブラウザーではなくNode.jsで実行されます。window、document、localStorageを参照するコードは、サーバー上でクラッシュします。typeof window !== 'undefined'で条件分岐します。
const isBrowser = typeof window !== 'undefined';
function getTheme() {
if (!isBrowser) return 'light'; // server-safe default
return localStorage.getItem('theme') ?? 'light';
}React Server ComponentsとSSRの違い
SSRはリクエストごとにコンポーネントをHTMLへレンダリングします。一方、React Server Components(RSC)は異なる仕組みで、JavaScriptをクライアントに一切送信せず、非同期にもできます。ただしRSCには、RSCプロトコルをサポートするバンドラー(Next.jsなど)が必要です。
ストリーミングによるパフォーマンス上の利点
ストリーミングSSRはシェルのTime To First Byte(TTFB)を改善し、選択的ハイドレーションを可能にします。ブラウザーは、ページ全体のHTMLのストリーミングが完了する前に、操作可能な部分を先にハイドレーションできます。
理解度チェック
サーバーのHTMLがすでに存在する場合、ReactのhydrateRoot()はcreateRoot()と異なり何をしますか。
まとめ
SSRでは、renderToPipeableStreamを使ってJavaScriptの読み込み前にHTMLを送信します。クライアントはhydrateRoot()を呼び出して、サーバーのHTMLにイベントリスナーを接続します。Suspenseを使ったストリーミングSSRでは、まずシェルを送信し、コンテンツを非同期で埋め込みます。ブラウザーAPIはtypeof window !== 'undefined'で条件分岐します。
よくある質問
「React SSRの内部動作」レッスンは無料ですか?
はい。「React SSRの内部動作」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、React Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 React Academyコースには全4レッスンが含まれています。
「React SSRの内部動作」で何を学びますか?
renderToString/renderToPipeableStreamの流れと、クライアントがどのようにhydrationを引き継ぐかを追います。 ブラウザで直接実行するハンズオンコードでReact Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
React Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのReact Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「React SSRの内部動作」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このReact Academyレッスンでコードを書いて実行できますか?
はい。すべてのReact Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。