URL: https://www.progressiverobot.com/react-server-side-rendering-ja/

はじめに

_サーバー側レンダリング_(SSR)は、サーバー上のクライアント側_シングルページアプリケーション_(SPA)をレンダリングし、完全にレンダリングされたページをクライアントに送信するための一般的な手法です。これにより、動的コンポーネントを静的HTMLマークアップとして提供できます。

このアプローチは、JavaScriptのインデックス作成が適切に行われない場合、検索エンジンの最適化(SEO)に役立ちます。また、大規模なJavaScriptバンドルのダウンロードが低速ネットワークによって損なわれている場合にも有効な場合があります。

このチュートリアルでは、Create React Appを使用しReactアプリを初期化した後、プロジェクトを変更してサーバー側レンダリングを有効にします。

このチュートリアルを終了すると、クライアント側 のReactアプリとサーバー側のExpressアプリを使用した作業プロジェクトが作成されます。

注意: 一方Next.jsは、Reactで構築された静的なサーバーレンダリングアプリケーションを作成するための最新のアプローチを提供します。

前提条件

react illustration for: 前提条件

このチュートリアルを実行するには、次のものが必要です。

このチュートリアルは、Node v14.4.0、npm v6.14.5で検証済です。

ステップ 1 — Reactアプリの作成とアプリコンポーネントの変更

まず、npxを用いて、最新バージョンのCreate React Appを使用して新しいReactアプリを起動します。

<^>my-ssr-app<^>アプリを呼び出しましょう。

				
					
npx create-react-app@3.4.1 &lt;^&gt;my-ssr-app&lt;^&gt;

				
			

次に、cdで新しいディレクトリに移動します。

				
					
cd &lt;^&gt;my-ssr-app&lt;^&gt;

				
			

最後に、インストールを確認するために、新しいクライアント側アプリを起動します。

				
					
npm start

				
			

ブラウザウィンドウに、サンプルReactアプリが表示されます。

それでは、<Home>コンポーネントを作成しましょう。

				
					
nano src/Home.js

				
			

次に、Home.jsファイルに次のコードを追加します。

				
					
[label src/Home.js]

import React from 'react';



export default props =&gt; {

 return &lt;h1&gt;Hello {props.name}!&lt;/h1&gt;;

};

				
			

これにより、nameに対して「Hello」メッセージが付いた<h1>見出しが作成されます。

次に、<App>コンポーネントで<Home>をレンダリングしましょう。App.jsファイルを開きます。

				
					
nano src/App.js

				
			

次に、既存のコード行をこれらの新しいコード行に置き換えます。

				
					
[label src/App.js]

import React from 'react';

&lt;^&gt;import Home from './Home';&lt;^&gt;



&lt;^&gt;export default () =&gt; {&lt;^&gt;

 &lt;^&gt;return &lt;Home name="Sammy" /&gt;;&lt;^&gt;

&lt;^&gt;};&lt;^&gt;

				
			

これにより、name<Home>コンポーネントに渡されるため、メッセージは、「Hello <^>Sammy<^>!」と表示されるはずです。

このアプリのindex.jsファイルでは、サーバー側のレンダリング後にアプリを再ハイドレーションすることをDOMレンダラーに示すために、renderの代わりに、ReactDOMのhydrateメソッドを使用します。

index.jsファイルを開きましょう。

				
					
nano index.js

				
			

次に、index.jsファイルの内容を次のコードに置き換えます。

				
					
[label index.js]

import React from 'react';

import ReactDOM from 'react-dom';

import App from './App';



ReactDOM.hydrate(&lt;App /&gt;, document.getElementById('root'));

				
			

これで、クライアント側のセットアップは終了です。次にサーバー側のセットアップに進みます。

ステップ 2 — Expressサーバーの作成とアプリコンポーネントのレンダリング

アプリの準備が整ったので、レンダリングされたバージョンを送信するサーバーをセットアップしましょう。サーバーにはExpressを使用します。端末ウィンドウに次のコマンドを入力して、プロジェクトに追加しましょう。

				
					
npm install express&lt;^&gt;@4.17.1&lt;^&gt;

				
			

または、yarnを使用して次のように行います。

				
					
yarn add express&lt;^&gt;@4.17.1&lt;^&gt;

				
			

次に、アプリのsrcディレクトリの横にserverディレクトリを作成します。

				
					
mkdir server

				
			

次に、Expressサーバーコードを含む新しいindex.jsファイルを作成します。

				
					
nano server/index.js

				
			

いくつかの定数を必要とし、定義するインポートを追加します。

				
					
[label server/index.js]

import path from 'path';

import fs from 'fs';



import React from 'react';

import express from 'express';

import ReactDOMServer from 'react-dom/server';



import App from '../src/App';



const PORT = process.env.PORT || 3006;

const app = express();

				
			

次に、エラー処理を含むサーバーコードを追加します。

				
					
[label server/index.js]

// ...



app.get('/', (req, res) =&gt; {

 const app = ReactDOMServer.renderToString(&lt;App /&gt;);



 const indexFile = path.resolve('./build/index.html');

 fs.readFile(indexFile, 'utf8', (err, data) =&gt; {

 if (err) {

 console.error('Something went wrong:', err);

 return res.status(500).send('Oops, better luck next time!');

 }



 return res.send(

 data.replace('&lt;div id="root"&gt;&lt;/div&gt;', `&lt;div id="root"&gt;${app}&lt;/div&gt;`)

 );

 });

});



app.use(express.static('./build'));



app.listen(PORT, () =&gt; {

 console.log(`Server is listening on port ${PORT}`);

});

				
			

ご覧のとおり、<App>コンポーネントをサーバーのクライアントアプリから直接インポートすることができます。

ここでは3つの重要なことが起こっています。

  • buildディレクトリのコンテンツを静的ファイルとして提供するようにExpressに指示します。
  • ReactDOMServerrenderToStringメソッドを使用して、アプリを静的なHTML文字列にレンダリングします。
  • 次に、構築されたクライアントアプリから静的index.htmlファイルを読み取り、id「root」<div>にアプリの静的コンテンツを挿入し、リクエストへの応答として送信します。

ステップ 3 — webpack、Babel、およびnpmスクリプトの設定

サーバーコードを機能させるには、webpackBabelを使用して、サーバーコードをバンドルしてトランスパイルする必要があります。これを実行するには、端末ウィンドウに次のコマンド を入力して、プロジェクトに開発の依存関係を追加しましょう。

				
					
npm install webpack&lt;^&gt;@4.42.0&lt;^&gt; webpack-cli&lt;^&gt;@3.3.12&lt;^&gt; webpack-node-externals&lt;^&gt;@1.7.2&lt;^&gt; @babel/core&lt;^&gt;@7.10.4&lt;^&gt; babel-loader&lt;^&gt;@8.1.0&lt;^&gt; @babel/preset-env&lt;^&gt;@7.10.4&lt;^&gt; @babel/preset-react&lt;^&gt;@7.10.4&lt;^&gt; --save-dev

				
			

または、yarnを使用して次のように行います。

				
					
yarn add webpack&lt;^&gt;@4.42.0&lt;^&gt; webpack-cli&lt;^&gt;@3.3.12&lt;^&gt; webpack-node-externals&lt;^&gt;@1.7.2&lt;^&gt; @babel/core&lt;^&gt;@7.10.4&lt;^&gt; babel-loader&lt;^&gt;@8.1.0&lt;^&gt; @babel/preset-env&lt;^&gt;@7.10.4&lt;^&gt; @babel/preset-react&lt;^&gt;@7.10.4&lt;^&gt; --dev

				
			

注:このチュートリアルの以前のバージョンは、babel-corebabel-preset-env、およびbabel-preset-react-appをインストールしました。これらのパッケージはその後アーカイブされ、代わりにモノリポジトリのバージョンが使用されます。

次に、Babelの設定ファイルを作成します。

				
					
nano .babelrc.json

				
			

次に、envreact-appプリセットを追加します。

				
					
[label .babelrc.json]

{

 "presets": [

 "@babel/preset-env",

 "@babel/preset-react"

 ]

}

				
			

注意:このチュートリアルの以前のバージョンでは、.babelrcファイル(.jsonファイル拡張子なし)を使用していました。これはBabel 6の設定ファイルでしたが、Babel 7では当てはまりません。

次に、Babel Loaderを使用してコードをトランスパイルするサーバーのwebpack設定を作成します。ファイルの作成から始めます。

				
					
nano webpack.server.js

				
			

その後、webpack.server .jsファイルに次の設定を追加します。

				
					
[label webpack.server.js]

const path = require('path');

const nodeExternals = require('webpack-node-externals');



module.exports = {

 entry: './server/index.js',



 target: 'node',



 externals: [nodeExternals()],



 output: {

 path: path.resolve('server-build'),

 filename: 'index.js'

 },



 module: {

 rules: [

 {

 test: /\.js$/,

 use: 'babel-loader'

 }

 ]

 }

};

				
			

この設定により、トランスパイルされたサーバーバンドルは、index.jsというファイルのserver-buildフォルダに出力されます。

webpack-node-externals以降のtarget: 'node'externals: [nodeExternals()] の使用に注意してください。これは、バンドルのnode_modulesからファイルを除外します。サーバーはこれらのファイルに直接アクセスできます。

これにより、依存関係のインストールとwebpackおよびBabelの設定は完了です。

次に、package.jsonに再度アクセスして、ヘルパーnpmスクリプトを追加します。

				
					
nano package.json

				
			

SSRアプリケーションを簡単に構築して提供するために、dev:build-serverdev:startdev scriptsをpackage.jsonファイルに追加します。

				
					
[label package.json]

"scripts": {

 &lt;^&gt;"dev:build-server": "NODE_ENV=development webpack --config webpack.server.js --mode=development -w",&lt;^&gt;

 &lt;^&gt;"dev:start": "nodemon ./server-build/index.js",&lt;^&gt;

 &lt;^&gt;"dev": "npm-run-all --parallel build dev:*",&lt;^&gt;

 ...

},

				
			

サーバーに変更を加えた場合は、nodemonを使用してサーバーを再起動します。そして、npm-run-allを使用して複数のコマンドを並行して実行します。

端末ウィンドウで次のコマンドを入力して、これらのパッケージを今すぐインストールしましょう。

				
					
npm install nodemon&lt;^&gt;@2.0.4&lt;^&gt; npm-run-all&lt;^&gt;@4.1.5&lt;^&gt; --save-dev

				
			

または、yarnを使用して次のように行います。

				
					
yarn add nodemon&lt;^&gt;@2.0.4&lt;^&gt; npm-run-all&lt;^&gt;@4.1.5&lt;^&gt; --dev

				
			

このようにして、次のコマンドを実行して、クライアント側のアプリを構築し、サーバーコードをバンドルしてトランスパイルし、:3006でサーバーを起動できます。

				
					
npm run dev

				
			

または、yarnを使用して次のように行います。

				
					
yarn run dev

				
			

サーバーのwebpack設定により変更を監視し、サーバーは変更時に再起動します。ただし、クライアントアプリの場合は、現在のところ、変更を加えるたびに構築する必要があります。ここに、未解決の課題があります。

ここで、Webブラウザでhttp://localhost:3006/を開くと、サーバー側のレンダリングアプリが表示されます。

前回、ソースコードは次のように表示しました。

				
					
[secondary_label Output]

&lt;div id="root"&gt;&lt;/div&gt;

				
			

しかし今回は、変更を加えたことで、ソースコードは次のように表示します。

				
					
[secondary_label Output]

&lt;div id="root"&gt;&lt;h1 data-reactroot=""&gt;Hello &lt;!-- --&gt;Sammy&lt;!-- --&gt;!&lt;/h1&gt;&lt;/div&gt;

				
			

サーバー側のレンダリングにより、<App>コンポーネントがHTMLに正常に変換されました。

まとめ

このチュートリアルでは、Reactアプリを初期化し、サーバー側のレンダリングを有効にしました。

この投稿では、実行できることの内容に軽く触れただけです。ルーティング、データフェッチ、またはReduxもサーバーサイドのレンダリングアプリの一部になる必要があると、作業は少し複雑になりがちです。

SSRの使用の主な利点の1つは、JavaScriptコードを実行しないクローラーでも、コンテンツをクロールできるアプリケーションがあることです。これは、検索エンジン最適化(SEO)と、ソーシャルメディアチャネルへのメタデータの提供に役立ちます。

最初のリクエストで、サーバーから完全にロードされたアプリケーションが送信されるため、SSRはパフォーマンスの向上にもとても役立ちます。重要なアプリケーションの場合、SSRには少し複雑になる可能性のあるセットアップが必要であり、サーバーに大きな負荷がかかるため、有用性が異なる場合があります。Reactアプリにサーバー側のレンダリングを使用するかどうかは、特定のニーズと、どのトレイドオフがユースケースにとって最適であるかに依存します。

React について詳しく知りたい場合は、How To Code in React.js(React.js のコーディング方法) シリーズを参照するか、演習とプログラミングプロジェクトの React トピックページをご覧ください。