Reactアプリのサーバー側レンダリングを有効にする方法
警告:このチュートリアルは、ReactDOM.hydrate()
およびReactDOMServer.rendertoString()
の簡単な紹介を目的としています。 実稼働での使用を目的としたものではありません。
あるいは、 Next.js は、Reactで構築された静的なサーバーレンダリングアプリケーションを作成するための最新のアプローチを提供します。
序章
サーバー側レンダリング(SSR)は、サーバー上でクライアント側シングルページアプリケーション(SPA)をレンダリングし、完全にレンダリングされたページをクライアントに送信するための一般的な手法です。 これにより、動的コンポーネントを静的HTMLマークアップとして提供できます。
このアプローチは、インデックス作成でJavaScriptが適切に処理されない場合の検索エンジン最適化(SEO)に役立ちます。 また、低速のネットワークによって大きなJavaScriptバンドルのダウンロードが損なわれる状況でも役立つ場合があります。
このチュートリアルでは、 Create React App を使用してReactアプリを初期化し、プロジェクトを変更してサーバー側のレンダリングを有効にします。
このチュートリアルの最後に、クライアント側のReactアプリケーションとサーバー側のExpressアプリケーションを使用した作業プロジェクトがあります。
前提条件
このチュートリアルを完了するには、次のものが必要です。
- Node.jsはローカルにインストールされます。これは、Node.jsのインストール方法とローカル開発環境の作成に従って実行できます。
このチュートリアルは、ノードv16.13.1、npm
v8.1.2、react
v17.0.2、@babel/core
v7.16.0、webpack
v4.44.2、 express
v4.17.1、nodemon
v2.0.15、およびnpm-run-all
v4.1.5。
ステップ1—Reactアプリの作成とアプリコンポーネントの変更
まず、 npx を使用して、最新バージョンのCreateReactAppを使用して新しいReactアプリを起動します。
アプリをreact-ssr-exampleと呼びましょう。
- npx create-react-app react-ssr-example
次に、cd
を新しいディレクトリに追加します。
cd react-ssr-example
最後に、インストールを確認するために、新しいクライアント側アプリを起動します。
- npm start
ブラウザウィンドウに表示されるReactアプリの例をご覧ください。
次に、src
ディレクトリに、新しい<Home>
コンポーネントを作成しましょう。
- nano src/Home.js
次に、Home.js
ファイルに次のコードを追加します。
function Home(props) {
return <h1>Hello {props.name}!</h1>;
};
export default Home;
これにより、名前に向けられた"Hello"
メッセージを含む<h1>
見出しが作成されます。
次に、<App>
コンポーネントで<Home>
をレンダリングしましょう。 src
ディレクトリにあるApp.js
ファイルを開きます。
- nano src/App.js
次に、既存のコード行を次の新しいコード行に置き換えます。
import Home from './Home';
function App() {
return <Home name="Sammy"/>;
}
export default App;
これにより、name
から<Home>
コンポーネントが渡されるため、表示されるメッセージは次のようになります。
Output"Hello Sammy!"
アプリのindex.js
ファイルでは、render
の代わりにReactDOMのハイドレートメソッドを使用して、サーバー側のレンダリング後にアプリをリハイドレートすることをDOMレンダラーに示します。 。
src
ディレクトリにあるindex.js
ファイルを開きましょう。
- nano src/index.js
次に、index.js
ファイルの内容を次のコードに置き換えます。
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.hydrate(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
これでクライアント側の設定は終了です。サーバー側の設定に進むことができます。
ステップ2—Expressサーバーの作成とアプリコンポーネントのレンダリング
アプリが配置されたので、レンダリングされたバージョンを送信するサーバーを設定しましょう。 サーバーにはExpressを使用します。
注: Create React Appのreact-scripts
は、webpack-dev-server
の要件としてexpress
のバージョンをインストールします。 依存関係ツリーの競合を回避するために、このチュートリアルにはこのインストール手順は含まれていません。
次に、プロジェクトのルートディレクトリに新しいserver
ディレクトリを作成します。
- mkdir server
次に、server
ディレクトリ内に、Expressサーバーコードを含む新しいindex.js
ファイルを作成します。
- nano server/index.js
いくつかの定数を必要とし、定義するインポートを追加します。
import path from 'path';
import fs from 'fs';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import express from 'express';
import App from '../src/App';
const PORT = process.env.PORT || 3006;
const app = express();
次に、エラー処理を含むサーバーコードを追加します。
// ...
app.get('/', (req, res) => {
const app = ReactDOMServer.renderToString(<App />);
const indexFile = path.resolve('./build/index.html');
fs.readFile(indexFile, 'utf8', (err, data) => {
if (err) {
console.error('Something went wrong:', err);
return res.status(500).send('Oops, better luck next time!');
}
return res.send(
data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
);
});
});
app.use(express.static('./build'));
app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
<App>
コンポーネントをクライアントアプリからサーバーから直接インポートすることができます。
ここでは3つの重要なことが起こっています。
- Expressは、
build
ディレクトリのコンテンツを静的ファイルとして提供するために使用されます。 ReactDOMServer
のrenderToString
は、アプリを静的なHTML文字列にレンダリングするために使用されます。- ビルドされたクライアントアプリから静的
index.html
ファイルが読み取られます。 アプリの静的コンテンツは、"root"
のid
で<div>
に挿入されます。 これは、リクエストへの応答として送信されます。
ステップ3— webpack、Babel、およびnpm
スクリプトの構成
サーバーコードを機能させるには、webpackとBabelを使用して、サーバーコードをバンドルしてトランスパイルする必要があります。 これを達成するため。
注:このチュートリアルの以前のバージョンでは、babel-core
、babel-preset-env
、およびbabel-preset-react-app
がインストールされていました。 その後、これらのパッケージはアーカイブされ、代わりにモノレポジトリバージョンが利用されました。
Create ReactAppのreact-scripts
は、次のパッケージのインストールを処理します:webpack
、webpack-cli
、webpack-node-externals
、@babel/core
、@babel/preset-env
、@babel/preset-react
。 依存関係ツリーの競合を回避するために、このチュートリアルにはこのインストール手順は含まれていません。
次に、プロジェクトのルートディレクトリに新しいBabel構成ファイルを作成します。
- nano .babelrc.json
次に、env
およびreact-app
プリセットを追加します。
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
注:このチュートリアルの以前のバージョンでは、.babelrc
ファイル(.json
ファイル拡張子なし)を使用していました。 これはBabel6の構成ファイルでしたが、Babel7には当てはまりません。
次に、BabelLoaderを使用してコードをトランスパイルするサーバーのwebpack構成を作成します。 プロジェクトのルートディレクトリにwebpack.server.js
ファイルを作成することから始めます。
- nano webpack.server.js
次に、次の構成を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
dev:build-server
、dev:start
、およびdev
スクリプトをpackage.json
ファイルに追加して、SSRアプリケーションをビルドして提供します。
"scripts": {
"dev:build-server": "NODE_ENV=development webpack --config webpack.server.js --mode=development -w",
"dev:start": "nodemon ./server-build/index.js",
"dev": "npm-run-all --parallel build dev:*",
// ...
},
dev:build-server
スクリプトは、環境を"development"
に設定し、前に作成した構成ファイルを使用してwebpack
を呼び出します。 dev:start
スクリプトは、nodemon
を呼び出して、ビルドされた出力を提供します。
dev
スクリプトは、npm-run-all
を呼び出して、parallel
build
スクリプトおよびdev:*
で始まるすべてのスクリプト(dev:start
。
注: package.json
ファイル。
nodemon
は、変更が加えられたときにサーバーを再起動するために使用されます。 npm-run-all
は、複数のコマンドを並行して実行するために使用されます。
ターミナルウィンドウに次のコマンドを入力して、これらのパッケージをインストールしましょう。
- npm install nodemon@2.0.15 --save-dev
- npm install npm-run-all@4.1.5 --save-dev
これを実行すると、次のコマンドを実行してクライアント側アプリをビルドし、サーバーコードをバンドルしてトランスパイルし、:3006
でサーバーを起動できます。
- npm run dev
サーバーのwebpack構成は変更を監視し、サーバーは変更時に再起動します。 ただし、クライアントアプリの場合、変更を加えるたびに手動で再構築する必要があります。
注:ここに未解決の問題があります。
次に、Webブラウザーでhttp://localhost:3006/
を開き、サーバー側でレンダリングされたアプリを確認します。
以前は、ソースコードを表示すると次のことが明らかになりました。
Output<div id="root"></div>
しかし今、あなたが行った変更により、ソースコードは次のことを明らかにします。
Output<div id="root"><h1 data-reactroot="">Hello <!-- -->Sammy<!-- -->!</h1></div>
サーバー側のレンダリングは、<App>
コンポーネントをHTMLに正常に変換しました。
結論
このチュートリアルでは、Reactアプリケーションを初期化し、サーバー側のレンダリングを有効にしました。
この投稿では、可能な限り表面をかじっただけです。 ルーティング、データフェッチ、またはReduxもサーバー側でレンダリングされたアプリの一部である必要があると、状況は少し複雑になる傾向があります。
SSRを使用する主な利点の1つは、JavaScriptコードを実行しないクローラーであっても、コンテンツをクロールできるアプリがあることです。 これは、検索エンジン最適化(SEO)とソーシャルメディアチャネルへのメタデータの提供に役立ちます。
SSRは、完全にロードされたアプリが最初のリクエストでサーバーから送信されるため、パフォーマンスにも役立つことがよくあります。 重要なアプリの場合、SSRには少し複雑になる可能性のあるセットアップが必要であり、サーバーに大きな負荷がかかるため、マイレージは異なる場合があります。 Reactアプリにサーバー側のレンダリングを使用するかどうかは、特定のニーズと、ユースケースに最も適したトレードオフによって異なります。
Reactの詳細については、 React.js シリーズのコーディング方法をご覧になるか、Reactトピックページで演習やプログラミングプロジェクトを確認してください。