React SSRフレームワークをゼロから構築する
Grace Collins
Solutions Engineer · Leapcell

Reactによるサーバーサイドレンダリングの紹介
Web開発のダイナミックな状況において、ユーザーエクスペリエンスと検索エンジン最適化(SEO)は最優先事項です。Reactのようなライブラリで構築された最新のシングルページアプリケーション(SPA)は、リッチでインタラクティブな体験を提供しますが、初期ロードパフォーマンスや検索エンジンインデックス作成においてしばしば課題に直面します。そこで、サーバーサイドレンダリング(SSR)が登場します。SSRを使用すると、サーバー上でReactコンポーネントを静的なHTMLにレンダリングし、完全に整形されたページをクライアントに送信できます。このアプローチは、ユーザーがすぐにコンテンツを見ることができ、検索エンジンが好むクロール可能なHTML構造を提供するため、知覚されるロード時間を大幅に向上させます。この記事では、シンプルでありながら機能的なReact SSRフレームワークをゼロから構築するプロセスをガイドし、その背後にあるメカニズムを解明し、その利点を活用できるようにします。
SSRのコアコンセプトの理解
実装に入る前に、React SSRに関わる主要な概念を明確に理解しましょう。
Reactコンポーネント
あらゆるReactアプリケーションの中心にはコンポーネントがあります。これらは、JSXを使用して記述された再利用可能で自己完結型のUIの一部です。SSRの場合、これらの同じコンポーネントがサーバー上でレンダリングされます。
ReactDOMServer
これはReactが提供する重要なパッケージであり、Reactコンポーネントを静的なHTML文字列にレンダリングできます。具体的には、renderToString
とrenderToStaticMarkup
が使用する関数です。renderToString
は、data-reactid
属性を含んでいるため、クライアントサイドでReactがコンポーネントを「ハイドレート」できるようになり、全体的なDOMを再レンダリングすることなくインタラクティブになるため、一般的に推奨されます。
クライアントサイドハイドレーション
サーバーがHTMLを送信した後、クライアントサイドのReactコードがこの事前にレンダリングされたHTMLに「アタッチ」します。ハイドレーションとして知られるこのプロセスは、静的なHTMLを完全にインタラクティブなReactアプリケーションに変換し、サーバーでレンダリングされたDOM構造を維持し、ちらつきや再レンダリングを回避します。
Express.js
Node.js用のミニマリストWebフレームワークであるExpress.jsは、HTTPリクエストを処理し、Reactアプリケーションをレンダリングし、結果のHTMLをクライアントに送信するサーバーとして機能します。
Babel
JSXや最新のJavaScript機能を使用してReactコンポーネントを記述するため、BabelはコードをNode.jsおよびクライアントサイドブラウザが理解できる形式にトランスパイルするために不可欠です。
ミニマルなReact SSRフレームワークの構築
ステップバイステップでSSRフレームワークの構築を開始しましょう。
プロジェクトセットアップ
まず、新しいプロジェクトディレクトリを作成し、初期化します。
mkdir react-ssr-framework cd react-ssr-framework npm init -y
次に、必要な依存関係をインストールします。
npm install react react-dom express @babel/core @babel/preset-env @babel/preset-react babel-loader webpack webpack-node-externals npm install --save-dev nodemon
サーバー(Node.js機能を使用し、ブラウザの互換性についてそれほど気にする必要がない)とクライアントの両方に、2つの別々のBabel設定が必要です。簡単にするために、この例では両方で単一の.babelrc
を使用し、クライアントサイドコードのブラウザ互換性を確保します。
.babelrc
ファイルを作成します。
{ "presets": ["@babel/preset-env", "@babel/preset-react"] }
最初のReactコンポーネント
サーバーとクライアントの両方でレンダリングされるシンプルなApp.js
コンポーネントを作成しましょう。
// src/components/App.js import React from 'react'; const App = ({ message }) => { return ( <div> <h1>Hello from React SSR!</h1> <p>{message}</p> <button onClick={() => alert('This is an interactive button!')}> Click Me </button> </div> ); }; export default App;
サーバーサイドレンダリングロジック
Express.jsを使用してApp
コンポーネントをレンダリングするサーバーをセットアップしましょう。
// src/server/index.js import express from 'express'; import React from 'react'; import ReactDOMServer from 'react-dom/server'; import App from '../components/App'; const app = express(); app.get('/', (req, res) => { const initialProps = { message: 'This content was rendered on the server!' }; const appString = ReactDOMServer.renderToString(<App {...initialProps} />); res.send(` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>React SSR App</title> </head> <body> <div id="root">${appString}</div> <script> window.__INITIAL_PROPS__ = ${JSON.stringify(initialProps)}; </script> <script src="/client_bundle.js"></script> </body> </html> `); }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); });
いくつかの重要な詳細に注意してください。
- HTML文字列を取得するために
ReactDOMServer.renderToString
を使用しています。 appString
をHTMLレスポンスに直接埋め込んでいます。window.__INITIAL_PROPS__
は、サーバーレンダリングに使用されたものと同じプロップを保持するグローバル変数です。これはクライアントサイドハイドレーションに不可欠です。script src="/client_bundle.js"
は、クライアントサイドJavaScriptバンドルを提供することを示しています。
クライアントサイドハイドレーション
クライアントサイドバンドルは、アプリケーションを再ハイドレーションする責任を負います。
// src/client/index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from '../components/App'; // サーバーから送信された初期プロップを取得します const initialProps = window.__INITIAL_PROPS__; ReactDOM.hydrate(<App {...initialProps} />, document.getElementById('root'));
ここでは、ReactDOM.render
の代わりにReactDOM.hydrate
が使用されています。hydrate
は、Reactにすべてを再レンダリングするのではなく、既存のHTMLマークアップにアタッチしようと指示します。
Webpackによるバンドル
ブラウザに提供するためにクライアントサイドJavaScriptをバンドルする必要があります。また、Node.jsはプリプロセッシングなしではJSXやESモジュール(import
/export
)をネイティブで理解できないため、サーバーサイドコードをコンパイルする必要があります。
webpack.config.js
ファイルを作成します。
// webpack.config.js const path = require('path'); const nodeExternals = require('webpack-node-externals'); const clientConfig = { mode: 'development', entry: './src/client/index.js', output: { path: path.resolve(__dirname, 'public'), filename: 'client_bundle.js', }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', }, }, ], }, }; const serverConfig = { mode: 'development', target: 'node', // サーバーバンドルに不可欠 externals: [nodeExternals()], // node_modulesの依存関係をバンドルしないようにします entry: './src/server/index.js', output: { path: path.resolve(__dirname, 'build'), filename: 'server_bundle.js', }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', }, }, ], }, }; module.exports = [clientConfig, serverConfig];
2つの設定があります。clientConfig
はクライアントサイドコードをpublic/client_bundle.js
にバンドルし、serverConfig
はサーバーサイドコードをbuild/server_bundle.js
にバンドルします。serverConfig
のtarget: 'node'
およびexternals: [nodeExternals()]
は、Node.js環境にとって重要です。
静的アセットの提供
サーバーはクライアントサイドバンドルを提供する必要があります。src/server/index.js
に、app.get
ルートの前に次の行を追加します。
// src/server/index.js (この行を追加) app.use(express.static('public')); // 'public'ディレクトリから静的ファイルを配信します
npmスクリプト
アプリケーションをビルドして実行するためのスクリプトをpackage.json
に追加します。
// package.json { "name": "react-ssr-framework", // ... その他のフィールド "scripts": { "build:client": "webpack --config webpack.config.js --env.target=client", "build:server": "webpack --config webpack.config.js --env.target=server", "build": "webpack --config webpack.config.js", "start": "node ./build/server_bundle.js", "dev": "npm run build && nodemon --watch build --exec \"npm start\"" }, // ... その他のフィールド }
これで、npm run build
を実行して初期バンドルを作成し、次にnpm run dev
を実行してサーバーを自動再起動で起動します。
SSRのテスト
ブラウザでhttp://localhost:3000
に移動します。「Hello from React SSR!」と「This content was rendered on the server!」が表示されるはずです。重要なことに、ページソース(要素を検査するのではなく)を表示すると、サーバーサイドレンダリングを確認するReact HTMLがソースに直接埋め込まれていることがわかります。ボタンもインタラクティブで、クライアントサイドハイドレーションを示しています。
SSRのアプリケーションシナリオ
この基本的なフレームワークはシンプルですが、さまざまなシナリオに適用できるコア原則を示しています。
- SEOの向上: 検索エンジンクローラーは、事前にレンダリングされたHTMLを簡単に解析でき、インデックス作成とランキングが向上します。
- より高速な初回ロード: ユーザーは意味のあるコンテンツをより迅速に表示でき、知覚されるパフォーマンスが向上し、離脱率が低下します。
- より良いアクセシビリティ: 初期HTMLがすぐに利用可能になるため、低速なインターネット接続や古いデバイスのユーザーに役立つ場合があります。
- Open Graphタグとソーシャル共有: ソーシャルメディア共有(例:og
、og)の特定のメタデータは、特定のページコンテンツに基づいてサーバー上で動的にレンダリングできます。
結論
Reactコンポーネント、サーバーレンダリング用のReactDOMServer、提供用のExpress.js、およびクライアントサイドハイドレーションの重要な相互作用を実証する、基本的なReact SSRフレームワークを正常に構築しました。このセットアップは、静的な初期ビューをインタラクティブなアプリケーションに変換し、パフォーマンスとSEOの向上を達成する方法の基本的な理解を提供します。サーバーでHTMLをレンダリングし、次にクライアントでハイドレーションすることにより、優れたユーザーエクスペリエンスを提供し、検索エンジンアルゴリズムを満足させ、SSRをモダンで高性能なWeb開発の基盤としています。