警告:このチュートリアルは、ReactDOM.hydrate()およびReactDOMServer.rendertoString()の簡単な紹介を目的としています。 実稼働での使用を目的としたものではありません。

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

序章

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

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

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

このチュートリアルの最後に、クライアント側のReactアプリケーションとサーバー側のExpressアプリケーションを使用した作業プロジェクトがあります。

前提条件

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

このチュートリアルは、ノード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-allv4.1.5。

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

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

アプリをreact-ssr-exampleと呼びましょう。

  1. npx create-react-app react-ssr-example

次に、cdを新しいディレクトリに追加します。

cd react-ssr-example

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

  1. npm start

ブラウザウィンドウに表示されるReactアプリの例をご覧ください。

次に、srcディレクトリに、新しい<Home>コンポーネントを作成しましょう。

  1. nano src/Home.js

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

src / Home.js
function Home(props) {
  return <h1>Hello {props.name}!</h1>;
};

export default Home;

これにより、名前に向けられた"Hello"メッセージを含む<h1>見出しが作成されます。

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

  1. nano src/App.js

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

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ファイルを開きましょう。

  1. nano src/index.js

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

src / 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ディレクトリを作成します。

  1. mkdir server

次に、serverディレクトリ内に、Expressサーバーコードを含む新しいindex.jsファイルを作成します。

  1. nano server/index.js

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

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();

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

server / index.js
// ...

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ディレクトリのコンテンツを静的ファイルとして提供するために使用されます。
  • ReactDOMServerrenderToStringは、アプリを静的なHTML文字列にレンダリングするために使用されます。
  • ビルドされたクライアントアプリから静的index.htmlファイルが読み取られます。 アプリの静的コンテンツは、"root"id<div>に挿入されます。 これは、リクエストへの応答として送信されます。

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

サーバーコードを機能させるには、webpackBabelを使用して、サーバーコードをバンドルしてトランスパイルする必要があります。 これを達成するため。

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

Create ReactAppのreact-scriptsは、次のパッケージのインストールを処理します:webpackwebpack-cliwebpack-node-externals@babel/core [X129X ]、@babel/preset-env@babel/preset-react。 依存関係ツリーの競合を回避するために、このチュートリアルにはこのインストール手順は含まれていません。

次に、プロジェクトのルートディレクトリに新しいBabel構成ファイルを作成します。

  1. nano .babelrc.json

次に、envおよびreact-appプリセットを追加します。

.babelrc.json
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ]
}

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

次に、BabelLoaderを使用してコードをトランスパイルするサーバーのwebpack構成を作成します。 プロジェクトのルートディレクトリにwebpack.server.jsファイルを作成することから始めます。

  1. nano webpack.server.js

次に、次の構成を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-externalstarget: 'node'externals: [nodeExternals()]を使用していることに注意してください。これにより、バンドル内のnode_modulesのファイルが省略されます。 サーバーはこれらのファイルに直接アクセスできます。

これで、依存関係のインストールとwebpackおよびBabelの構成が完了します。

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

  1. nano package.json

dev:build-serverdev:start、およびdevスクリプトをpackage.jsonファイルに追加して、SSRアプリケーションをビルドして提供します。

package.json
"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:*で始まるすべてのスクリプト( [を含む)で実行します。 X138X]およびdev:start

注: package.jsonファイル。

nodemonは、変更が加えられたときにサーバーを再起動するために使用されます。 npm-run-allは、複数のコマンドを並行して実行するために使用されます。

ターミナルウィンドウに次のコマンドを入力して、これらのパッケージをインストールしましょう。

  1. npm install nodemon@2.0.15 --save-dev
  2. npm install npm-run-all@4.1.5 --save-dev

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

  1. 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トピックページで演習やプログラミングプロジェクトを確認してください。