Reactアプリを作ると、一度はサーバーで描画できないかと考えるでしょう。
SEO、初回の速度、SNSのプレビューを考えると、クライアントだけの描画では不十分に感じます。
その解決手段の一つがSSR、
Server-Side Rendering
です。
このシリーズでは、React開発者の視点から、動作、適した用途、制約を段階的に確認します。
概念と必要性、実際の処理を整理し、React 18の
ストリーミングレンダリング
で何が改善されたかも見ていきます。
SSRは、
ブラウザーではなくサーバーでHTMLを作って送る方式
です。
通常のReact SPAではJavaScriptの実行後に内容が現れますが、SSRなら事前に描画したHTMLを送れます。
ユーザーは早く画面を見られ、検索エンジンもHTMLを直接読めるため、SEOにも有利です。
クライアント描画は、次のように動きます。
- ブラウザーがHTMLをダウンロードする。
- HTML内のJavaScriptバンドルを読み込む。
- JavaScriptを実行し、Reactコンポーネントを描画する。
- 画面を表示する。
回線や端末性能によっては、かなり時間がかかります。
SSRでは次のようになります。
- アクセス時にサーバーがReactコンポーネントを描画する。
- 作成したHTMLをクライアントへ送る。
- ブラウザーがHTMLを受け取り、すぐ表示する。
- Reactのhydrateが動き、操作できるアプリになる。
まず素早く見える画面を提供し、その後JavaScriptでアプリを完成させる方式です。
次のような用途で便利です。
SEOが重要なページ
:検索エンジンがJavaScriptを完全には実行できない場合があるため、HTMLを提供する方式が有利です。
初回の速度が重要なサービス
:事前描画したHTMLを送るため、最初の画面が早く見えます。
SNSでの共有
:FacebookやTwitterなどはHTMLのメタタグからプレビューを作るため、その情報も正確に渡せます。
たとえばブログへ導入すれば、ユーザー体験と検索での露出を両方改善できます。
もう少し具体的に見てみます。
- ユーザーがサイトへアクセスする。
- サーバーが要求を処理する。
- renderToStringやrenderToPipeableStreamでReactコンポーネントをHTMLへ変換する。
- HTMLをユーザーへ送る。
- ブラウザーが描画して表示する。
- JavaScriptをダウンロードし、hydrateが動いて操作可能になる。
重要なのは、
サーバーでHTMLを作り、クライアントのJavaScriptでアプリへ命を吹き込む過程
です。
次のパートでは描画の各段階と、React 18のrenderToPipeableStreamなどを詳しく分析します。
従来はHTML全体を一度に作ってから送っていました。
単純ですが、すべての生成が終わるまで応答を受け取れず、遅く感じる場合があります。
React 18は、
部分的に描画したHTMLを順次送る方式
、すなわち
ストリーミングSSR
を導入しました。
準備できた内容を先に送り、残りを段階的に見せることで、体験が改善します。
これを可能にするのが
renderToPipeableStreamです。
renderToPipeableStreamとは 🔗
renderToPipeableStreamは、React 18で導入されたSSR関数です。
サーバーでのHTML生成結果を、
ストリーミング方式
で出力できます。
準備済みのHTMLから少しずつ送り、未完成の部分は後で埋める仕組みです。
ページ全体を待たずに、一部の内容を先に見られます。
Express.jsとrenderToPipeableStreamを組み合わせた例です。
import express from 'express';
import React from 'react';
import { renderToPipeableStream } from 'react-dom/server';
import App from './App';
const app = express();
app.get('*', (req, res) => {
const { pipe } = renderToPipeableStream(<App />, {
onShellReady() {
res.setHeader('Content-Type', 'text/html');
pipe(res);
},
onError(err) {
console.error(err);
}
});
});
onShellReadyはHTMLの基本構造が整うと呼ばれ、その時点からクライアントへ送信し始めます。
もう一つ重要なのが
Suspenseとの統合です。
データやコンポーネントの読み込み中に代替UIを表示する機能で、ストリーミングと組み合わせると、
準備のできた部分から順に表示
できます。
次の構成を考えます。
function App() {
return (
<div>
<Header />
<Suspense fallback={<div>Loading content...</div>}>
<MainContent />
</Suspense>
<Footer />
</div>
);
}
<Header />と
<Footer />はすぐ描画できても、
<MainContent />のデータ取得に時間がかかるとします。
ストリーミングSSRなら、HeaderとFooterを先に送って表示し、MainContentは準備ができた時点で埋めます。
ユーザーには段階的に画面が見えるため、快適に感じられます。
便利な方法ですが、注意も必要です。
- 代替UIが頻繁に出ると、かえって不自然に見える場合がある。
- Suspenseが深すぎたり依存関係が複雑だったりすると、性能の問題が生じうる。
- サーバー環境がストリーミング非対応なら利用できない。
そのため、
部分描画の区切りが明確な構造
に適用するのが効果的です。
SSRの概念と必要性、基本動作を確認しました。
さらに、React 18のストリーミングSSRの中心となる
renderToPipeableStreamとSuspenseの連携を紹介しました。
SSRは単なるHTML生成を超えて、ユーザー体験、SEO、性能を高める重要な技術です。
とくにデータ読み込みの多いページで、内容を早く見せるために役立ちます。
次回はhydration、Server Components、キャッシュ戦略などを詳しく扱います。