React サーバーサイドレンダリング原則の分析と実践

React サーバーサイドレンダリング原則の分析と実践

ほとんどの人は、サーバーサイド レンダリング (SSR と呼んでいます) の概念について聞いたことがあるでしょう。多くの学生は、すでに会社でサーバーサイド レンダリング プロジェクトを実施したことがあるかもしれません。Vue や React 開発プロジェクトなどの主流のシングルページ アプリケーションでは、通常、クライアントサイド レンダリング モデル (CSR と呼んでいます) が使用されます。

しかし、このモデルには2つの明らかな問題があります。1つ目は、TTFP時間が比較的長いことです。TTFPは最初の画面表示時間を指します。同時に、SEOランキングの条件を満たしておらず、検索エンジンでのランキングはあまり良くありません。したがって、いくつかのツールを使用してプロジェクトを改善し、シングルページ アプリケーションをサーバー側レンダリング プロジェクトに変換することで、これらの問題を解決することができます。

現在主流のサーバー側レンダリング フレームワーク (SSR フレームワークとも呼ばれます) は、Vue の場合は Nuxt.js、React の場合は Next.js です。ここでは、これらの SSR フレームワークは使用しませんが、その基本原則を理解するために、完全な SSR フレームワークをゼロから構築します。

サーバー上でReactコンポーネントを書く

クライアント側レンダリングの場合、まずブラウザがブラウザにリクエストを送信し、サーバーがページの html ファイルを返します。次に html が再度サーバーにリクエストを送信し、サーバーが js ファイルを返し、js ファイルがブラウザで実行されてページ構造が描画され、ブラウザにレンダリングされてページのレンダリングが完了します。

サーバー側レンダリングの場合は、プロセスが異なります。ブラウザがリクエストを送信し、サーバーが React コードを実行してページを生成し、サーバーが生成されたページをブラウザに返してレンダリングします。この場合、React コードはフロントエンドではなくサーバーの一部になります。

ここでコードを示します。まず、npm init でプロジェクトを初期化し、次に react、express、webpack、webpack-cli、webpack-node-externals をインストールする必要があります。

まず、React コンポーネントを記述します。 .src/components/Home/index.js では、js は node 環境で実行されるため、CommonJS 仕様に従い、インポートとエクスポートに require と module.exports を使用する必要があります。

React は、次のコードで定義されます。

const ホーム = () => {
  <div>ホーム</div>に戻る
}

モジュール.エクスポート = {
  デフォルト: ホーム
};

ここで開発した Home コンポーネントは、Node で直接実行することはできません。Node.js が認識できるように、webpack ツールを使用して jsx 構文を js 構文にパッケージ化してコンパイルする必要があります。webpack.server.js ファイルを作成する必要があります。

サーバー側で webpack を使用する場合は、ターゲットをノードとしてキーと値のペアを追加する必要があります。パスがサーバー側で使用される場合、js にパッケージ化する必要がないことはわかっています。パスがブラウザ側で使用される場合、js にパッケージ化する必要があります。したがって、サーバー側とブラウザ側でコンパイルする必要がある js は完全に異なります。したがって、パッケージ化するときに、サーバー側のコードとブラウザ側のコードのどちらをパッケージ化するかを webpack に指示する必要があります。

エントリ ファイルは、ノードの起動ファイルです。ここでは、./src/index.js と記述します。出力ファイルの名前は bundle で、ディレクトリは次のディレクトリの build フォルダーにあります。

定数Path = require('path');
const NodeExternals = require('webpack-node-externals'); // サーバー上で webpack を実行するには、NodeExternals を実行する必要があります。これにより、express などのノード モジュールが js にパッケージ化されなくなります。

モジュール.エクスポート = {
  ターゲット: 'ノード',
  モード: '開発'、
  エントリ: './src/server/index.js',
  出力: {
    ファイル名: 'bundle.js',
    パス: Path.resolve(__dirname, 'build')
  },
  外部: [NodeExternals()],
  モジュール: {
    ルール:
      {
        テスト: /.js?$/,
        ローダー: 'babel-loader',
        除外: /node_modules/、
        オプション:
          プリセット: ['react', 'stage-0', ['env', {
            ターゲット: {
              ブラウザ: ['最後の 2 つのバージョン']
            }
          }]]
        }
      }
    ]
  }
}

依存モジュールをインストールする

npm で babel-loader と babel-core をインストールし、 babel-preset-react と babel-preset-stage-0 と babel-preset-env --save を実行します。

次に、express モジュールに基づいて簡単なサービスを作成します。 ./src/server/index.js

var express = require('express');
var app = express();
const Home = require('../Components/Home');
app.get('*', 関数(req, res) {
  res.send(`<h1>hello</h1>`);
})

var server = app.listen(3000);

実行するには、webpack.server.js 構成ファイルを使用して webpack を実行します。

webpack --config webpack.server.js

パッケージ化後、ディレクトリに bundle.js が表示されます。この js は、パッケージ化によって生成された最終的な実行可能コードです。ノードを使用してこのファイルを実行し、ポート 3000 でサーバーを起動できます。このサービスにアクセスするには、127.0.0.1:3000 にアクセスし、ブラウザに Hello と出力されていることを確認します。

ノード ./build/bundile.js

上記のコードは実行前に webpack を使用してコンパイルされるため、ES モジュール仕様がサポートされ、CommonJS の使用が強制されなくなります。

src/components/Home/index.js

'react' から React をインポートします。

const ホーム = () => {
  <div>ホーム</div>に戻る
}

デフォルトのホームをエクスポートします。

/src/server/index.js の Home コンポーネントを使用できます。ここでは、まず react-dom をインストールし、renderToString を使用して Home コンポーネントをラベル文字列に変換する必要があります。もちろん、ここでは React に依存する必要があるため、React を導入する必要があります。

'express' から express をインポートします。
'../Components/Home' から Home をインポートします。
'react' から React をインポートします。
'react-dom/server' から renderToString をインポートします。

express() は、定数です。
const コンテンツ = renderToString(<Home />);
app.get('*', 関数(req, res) {
  res.send(`
    <html>
      <body>${コンテンツ}</body>
    </html>
  `);
})

var server = app.listen(3000);

# webpack を再パッケージ化 --config webpack.server.js
# サービスノード ./build/bundile.js を実行します

この時点で、ページには React コンポーネントのコードが表示されます。

React のサーバー側レンダリングは仮想 DOM に基づいており、サーバー側レンダリングによりページの最初の画面レンダリングが大幅に高速化されます。ただし、サーバーサイドレンダリングにもデメリットがあります。クライアントサイドレンダリングではReactコードがブラウザ側で実行されるため、ユーザーのブラウザ側のパフォーマンスを消費しますが、サーバーサイドレンダリングではReactコードがサーバー上で実行されるため、サーバー側のパフォーマンスを消費します。 React コードは多くのコンピューティング パフォーマンスを消費するため、サーバーのパフォーマンスが大幅に消費されます。

プロジェクトで SEO 最適化を使用する必要がなく、プロジェクトのアクセス速度がすでに非常に速い場合は、コストが依然として比較的高いため、SSR テクノロジを使用しないことをお勧めします。

上記のコードを変更するたびに、webpack のパッケージングを再実行してサーバーを起動する必要がありますが、これはデバッグするには面倒すぎます。この問題を解決するには、webpack の自動パッケージングとノードの再起動を行う必要があります。 package.json にビルド コマンドを追加し、--watch を使用して自動パッケージ化のファイルの変更を監視します。

{
  ...
  「スクリプト」: {
    "ビルド": "webpack --config webpack.server.js --watch"
  }
  ...
}

再パッケージ化だけでは不十分で、ノード サーバーを再起動する必要もあります。ここでは、nodemon モジュールを使用する必要があります。ここでは、nodemon のグローバル インストールを使用し、package.json ファイルに start コマンドを追加してノード サーバーを起動します。 nodemon を使用してビルド ファイルを監視し、変更があった場合は "node ./build/bundile.js" を再実行します。ここでは二重引用符を保持し、翻訳するだけです。

{
  ...
  「スクリプト」: {
    "開始": "nodemon --watch ビルド --exec node \"./build/bundile.js\"",
    "ビルド": "webpack --config webpack.server.js --watch"
  }
  ...
}

この時点でサーバーを起動します。ビルド後は他のコマンドは許可されないため、ここでは 2 つのウィンドウで次のコマンドを実行する必要があります。

npm 実行ビルド
npm 実行開始

この時点で、コードを変更すると、ページは自動的に更新されます。

しかし、上記のプロセスはまだ少し面倒です。コマンドを実行するには 2 つのウィンドウが必要です。両方のコマンドを 1 つのウィンドウで実行したいのです。グローバルにインストールできるサードパーティ モジュール npm-run-all を使用する必要があります。次に、package.json で変更します。

開発環境でパッケージ化とデバッグを行う必要があります。dev コマンドを作成し、その中で npm-run-all を実行します。--parallel は並列実行を意味し、dev: で始まるすべてのコマンドを実行します。開始とビルドの前に dev: を追加します。この時点で、サーバーを起動してファイルの変更をリッスンしたい場合は、npm run dev を実行するだけです。

{
  ...
  「スクリプト」: {
    "dev": "npm-run-all --parallel dev:**",
    "dev:start": "nodemon --watch ビルド --exec node \"./build/bundile.js\"",
    "dev:build": "webpack --config webpack.server.js --watch"
  }
  ...
}

同型性とは何ですか?

たとえば、次のコードでは、クリック時にクリックプロンプトがポップアップ表示されるように、クリックイベントを div にバインドします。しかし、実行後、サーバーがイベントをバインドできないため、このイベントはバインドされていないことがわかります。

src/components/Home/index.js

'react' から React をインポートします。

const ホーム = () => {
  <div onClick={() => { alert('click'); }}>ホーム</div> を返します。
}

デフォルトのホームをエクスポートします。

通常は、まずページをレンダリングし、次に従来の React プロジェクトと同様にブラウザ側で同じコードを実行して、クリック イベントが利用できるようにします。

これは同型性の概念につながります。私の理解では、React コードのセットはサーバー上で一度実行され、クライアント上で再度実行されます。

Isomorphism は無効なクリック イベントの問題を解決できます。まず、サーバーは 1 回実行することでページを正常に表示でき、クライアントは再度実行することでイベントをバインドできます。

ページがレンダリングされるときに index.js をロードし、app.use を使用して静的ファイルへのアクセス パスを作成すると、アクセスされた index.js が /public/index.js ファイルに要求されます。

app.use(express.static('public'));

app.get('/', 関数(req, res) {
  res.send(`
    <html>
      <本文>
        <div id="root">${コンテンツ}</div>
        <script src="/index.js"></script>
      </本文>
    </html>
  `);
})

公開/index.js

コンソールログ('public');

この状況に基づいて、ブラウザで React コードを 1 回実行できます。ここで新しい /src/client/index.js を作成します。クライアントによって実行されたコードを貼り付けます。ここでは、同型コードで render の代わりに hydrate を使用します。

'react' から React をインポートします。
'react-dom' から ReactDOM をインポートします。

'../Components/Home' から Home をインポートします。

ReactDOM.hydrate(<Home />, document.getElementById('root'));

次に、ルート ディレクトリに webpack.client.js ファイルを作成する必要があります。エントリファイルは./src/client/index.js、エクスポートファイルはpublic/index.jsです。

定数Path = require('path');

モジュール.エクスポート = {
  モード: '開発'、
  エントリ: './src/client/index.js',
  出力: {
    ファイル名: 'index.js',
    パス: Path.resolve(__dirname, 'public')
  },
  モジュール: {
    ルール:
      {
        テスト: /.js?$/,
        ローダー: 'babel-loader',
        除外: /node_modules/、
        オプション:
          プリセット: ['react', 'stage-0', ['env', {
            ターゲット: {
              ブラウザ: ['最後の 2 つのバージョン']
            }
          }]]
        }
      }
    ]
  }
}

package.jsonファイルにクライアントディレクトリをパッケージ化するコマンドを追加します。

{
  ...
  「スクリプト」: {
    "dev": "npm-run-all --parallel dev:**",
    "dev:start": "nodemon --watch ビルド --exec node \"./build/bundile.js\"",
    "dev:build": "webpack --config webpack.server.js --watch",
    "dev:build": "webpack --config webpack.client.js --watch",
  }
  ...
}

この方法では、クライアントの起動時に実行されるファイルをコンパイルします。ページに再度アクセスすると、イベントをバインドできます。

次に、上記プロジェクトのコードを整理します。上記の webpack.server.js ファイルと webpack.client.js ファイルには重複が多くあります。webpack-merge プラグインを使用してコンテンツをマージできます。

webpack.base.js

モジュール.エクスポート = {
  モジュール: {
    ルール:
      {
        テスト: /.js?$/,
        ローダー: 'babel-loader',
        除外: /node_modules/、
        オプション:
          プリセット: ['react', 'stage-0', ['env', {
            ターゲット: {
              ブラウザ: ['最後の 2 つのバージョン']
            }
          }]]
        }
      }
    ]
  }
}

webpack.server.js

定数Path = require('path');
const NodeExternals = require('webpack-node-externals'); // サーバー上で webpack を実行するには、NodeExternals を実行する必要があります。これにより、express などのノード モジュールが js にパッケージ化されなくなります。

'webpack-merge' を require してマージします。
config は './webpack.base.js' を必要とします。

定数serverConfig = {
  ターゲット: 'ノード',
  モード: '開発'、
  エントリ: './src/server/index.js',
  出力: {
    ファイル名: 'bundle.js',
    パス: Path.resolve(__dirname, 'build')
  },
  外部: [NodeExternals()],
}

module.exports = merge(config, serverConfig);

webpack.client.js

定数Path = require('path');
'webpack-merge' を require してマージします。
config は './webpack.base.js' を必要とします。

定数クライアント設定 = {
  モード: '開発'、
  エントリ: './src/client/index.js',
  出力: {
    ファイル名: 'index.js',
    パス: Path.resolve(__dirname, 'public')
  }
};

module.exports = merge(config, clientConfig);

サーバー上で実行されるコードは src/server に配置され、ブラウザ上で実行される js は src/client に配置されます。

React サーバーサイドレンダリングの分析と実践に関するこの記事はこれで終わりです。React サーバーサイドレンダリングに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き閲覧してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Reactのサーバーサイドレンダリング(同型性)の手法を詳しく解説
  • Reactサーバーサイドレンダリング入門から習得まで徹底解説
  • Reactサーバーサイドレンダリングに最適なソリューションの詳細な説明
  • Node を使用して reactSSR サーバーサイド レンダリング アーキテクチャを構築する
  • サーバー上でのReactレンダリングの実装の詳細な説明
  • React サーバーサイドレンダリング (概要)
  • React サーバーサイドレンダリングと同型実装

<<:  CentOS6.8 は cmake を使用して MySQL5.7.18 をインストールします。

>>:  Kali Linux Vmware 仮想マシンのインストール (図とテキスト)

推薦する

Alibaba Cloud Server に MySQL データベースをインストールする詳細なチュートリアル

目次序文1. MySQLをアンインストールする2. MySQLをインストールする要約する序文学習中に...

Alibaba Cloud ESC に MYSQL8.0 をインストールするチュートリアル

接続ツールを開きます。私はMobaXterm_Personal_12.1を使用します(公式サイトのダ...

Ubuntu システムログで /var/log/messages を設定する方法

1. 問題の説明今日、システム ログ ファイルを確認する必要がありますが、/var/log/mess...

HTML Web ページ リスト タグ学習チュートリアル

HTML Web ページ リスト タグの学習チュートリアル。 HTML ページでは、リストはアウトラ...

HTMLフォーム送信方法のケーススタディ

フォームの送信方法をまとめると次のようになります。 1. 送信ボタンを使用して送信します。送信ボタン...

JS 1次元配列を3次元配列に変換する例

今日、CSDN の Q&A セクションで友人が質問をしているのを見ました。彼は 1 次元配列...

MySQL で置換操作を使用したときにデータ損失が発生する問題の解決策

序文同社の開発者は、データの更新時に replace into ステートメントを使用していました。不...

Mysql と Oracle でよく使用される複数テーブルの変更ステートメントの概要

今日、SQLトレーニングの質問バンクでこの質問を見ました。これは、非常に代表的なマルチテーブル変更の...

Linux で最も頻繁に使用されるターミナル コマンドのトップ 10 のリストを取得します。

私が最も頻繁に使用するコマンドは次の通りです:選択肢CDギットls ssh須藤数週間前、私はこの R...

Nginx 環境での WordPress マルチサイト構成の詳細な説明

WordPress のマルチサイト機能を使用すると、1 つの WordPress プログラムをインス...

Docker イメージ管理の一般的な操作コード例

ミラーリングも Docker のコアコンポーネントの 1 つです。ミラーリングはコンテナ操作の基盤で...

JSコンストラクタとインスタンス化およびプロトタイプ導入の関係

目次1. コンストラクタとインスタンス化2. コンストラクターとインスタンス化の関係は何ですか? 3...

Centos7 構成 fastdfs および nginx 分散ファイル ストレージ システムの実装プロセス分析

1. libfastcommon-1.0.43 をインストールします。インストール パッケージは h...

Vueバックグラウンド管理に多言語機能を追加する例

目次1.まず、main.jsページを設定します2. 対応するパスの下で言語パックを構成します。ここに...

重複リクエストを削除するAxiosのソリューションについての簡単な説明

目次1. 重複したリクエストをキャンセルする2. すべてのリクエストをクリーンアップするこのソリュー...