本番環境に対応した SSR React アプリケーションの構築
ミリ秒単位が重要な世界では、サーバー側のレンダリングはフロントエンド アプリケーションにとって不可欠な機能となっています。
このガイドでは、React を使用して本番環境に対応した SSR を構築するための基本的なパターンを説明します。 SSR (Next.js など) が組み込まれた React ベースのフレームワークの背後にある原則を理解し、独自のカスタム ソリューションを作成する方法を学びます。
提供されたコードは本番環境に対応しており、Dockerfile を含むクライアント部分とサーバー部分の両方の完全なビルド プロセスを備えています。この実装では、Vite を使用してクライアントと SSR コードを構築しますが、任意の他のツールを使用することもできます。 Vite は、クライアントの開発モード中にホットリロードも行います。
Vite を含まないこのセットアップのバージョンに興味がある場合は、お気軽にお問い合わせください。
目次
- SSRとは
-
アプリの作成
- Vite を初期化しています
- React コンポーネントを更新しています
- サーバーの作成
- ビルドの構成
- ルーティング
- ドッカー
- 結論
SSRとは
サーバーサイド レンダリング (SSR) は、サーバーが Web ページの HTML コンテンツを生成してからブラウザーに送信する Web 開発の手法です。 JavaScript が空の HTML シェルをロードした後にユーザーのデバイス上でコンテンツを構築する従来のクライアント側レンダリング (CSR) とは異なり、SSR は完全にレンダリングされた HTML をサーバーから直接配信します。
SSR の主な利点:
- SEO の改善: 検索エンジン クローラーは完全にレンダリングされたコンテンツを受信するため、SSR によりインデックス付けが向上し、 ランキング。
- ファーストペイントの高速化: サーバーが面倒な作業を処理するため、ユーザーは意味のあるコンテンツをほぼ即座に見ることができます。 レンダリング。
- パフォーマンスの向上: ブラウザーのレンダリング ワークロードを軽減することで、SSR はよりスムーズなエクスペリエンスを提供します。 古いデバイスや性能の低いデバイスを使用しているユーザー。
- サーバーからクライアントへのシームレスなデータ転送: SSR を使用すると、動的サーバー側データをクライアントに渡すことができます。 クライアントバンドルを再構築しています。
アプリの作成
SSR を使用したアプリのフローは次の手順に従います:
- テンプレート HTML ファイルを読み取ります。
- React を初期化し、アプリのコンテンツの HTML 文字列を生成します。
- 生成された HTML 文字列をテンプレートに挿入します。
- 完全な HTML をブラウザに送信します。
- クライアントで HTML タグを照合し、アプリケーションをハイドレートしてインタラクティブにします。
Vite を初期化しています
私は pnpm と React-swc-ts Vite テンプレートを使用することを好みますが、他のセットアップを選択することもできます。
pnpm create vite react-ssr-app --template react-swc-ts
依存関係をインストールします:
pnpm create vite react-ssr-app --template react-swc-ts
React コンポーネントの更新
典型的な React アプリケーションには、index.html の main.tsx エントリ ポイントが 1 つあります。 SSR では、サーバー用とクライアント用の 2 つのエントリ ポイントが必要です。
サーバーエントリーポイント
Node.js サーバーはアプリを実行し、React コンポーネントを文字列 (renderToString) にレンダリングすることで HTML を生成します。
pnpm install
クライアントエントリポイント
ブラウザはサーバーで生成された HTML をハイドレートし、JavaScript と接続してページをインタラクティブにします。
ハイドレーション は、サーバーによってレンダリングされた静的 HTML にイベント リスナーやその他の動的動作をアタッチするプロセスです。
// ./src/entry-server.tsx import { renderToString } from 'react-dom/server' import App from './App' export function render() { return renderToString(<App />) }
Index.html を更新しています
プロジェクトのルートにあるindex.htmlファイルを更新します。 プレースホルダーは、サーバーが生成された HTML を挿入する場所です。
// ./src/entry-client.tsx import { hydrateRoot } from 'react-dom/client' import { StrictMode } from 'react' import App from './App' import './index.css' hydrateRoot( document.getElementById('root')!, <StrictMode> <App /> </StrictMode>, )
サーバーに必要なすべての依存関係は、クライアント バンドルに含まれないように、開発依存関係 (devDependency) としてインストールする必要があります。
次に、プロジェクトのルートに ./server という名前のフォルダーを作成し、次のファイルを追加します。
メインサーバーファイルの再エクスポート
メインサーバーファイルを再エクスポートします。これにより、コマンドの実行がより便利になります。
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + React + TS</title> </head> <body> <div> <h3> Create Server </h3> <p>First, install the dependencies:<br> </p> <pre class="brush:php;toolbar:false">pnpm install -D express compression sirv tsup vite-node nodemon @types/express @types/compression
定数の定義
HTML_KEY 定数は、index.html のプレースホルダー コメントと一致する必要があります。他の定数は環境設定を管理します。
// ./server/index.ts export * from './app'
Expressサーバーの作成
開発環境と運用環境に異なる構成で Express サーバーをセットアップします。
// ./server/constants.ts export const NODE_ENV = process.env.NODE_ENV || 'development' export const APP_PORT = process.env.APP_PORT || 3000 export const PROD = NODE_ENV === 'production' export const HTML_KEY = `<!--app-html-->`
開発モードの構成
開発では、Vite のミドルウェアを使用してリクエストを処理し、ホットリロードでindex.html ファイルを動的に変換します。サーバーは React アプリケーションをロードし、リクエストごとに HTML にレンダリングします。
// ./server/app.ts import express from 'express' import { PROD, APP_PORT } from './constants' import { setupProd } from './prod' import { setupDev } from './dev' export async function createServer() { const app = express() if (PROD) { await setupProd(app) } else { await setupDev(app) } app.listen(APP_PORT, () => { console.log(`http://localhost:${APP_PORT}`) }) } createServer()
実稼働モードの構成
運用環境では、圧縮を使用してパフォーマンスを最適化し、sirv を使用して静的ファイルを提供し、事前構築されたサーバー バンドルを使用してアプリをレンダリングします。
// ./server/dev.ts import { Application } from 'express' import fs from 'fs' import path from 'path' import { HTML_KEY } from './constants' const HTML_PATH = path.resolve(process.cwd(), 'index.html') const ENTRY_SERVER_PATH = path.resolve(process.cwd(), 'src/entry-server.tsx') export async function setupDev(app: Application) { // Create a Vite development server in middleware mode const vite = await ( await import('vite') ).createServer({ root: process.cwd(), server: { middlewareMode: true }, appType: 'custom', }) // Use Vite middleware for serving files app.use(vite.middlewares) app.get('*', async (req, res, next) => { try { // Read and transform the HTML file let html = fs.readFileSync(HTML_PATH, 'utf-8') html = await vite.transformIndexHtml(req.originalUrl, html) // Load the entry-server.tsx module and render the app const { render } = await vite.ssrLoadModule(ENTRY_SERVER_PATH) const appHtml = await render() // Replace the placeholder with the rendered HTML html = html.replace(HTML_KEY, appHtml) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { // Fix stack traces for Vite and handle errors vite.ssrFixStacktrace(e as Error) console.error((e as Error).stack) next(e) } }) }
ビルドの構成
アプリケーションを構築するためのベスト プラクティスに従うには、不要なパッケージをすべて除外し、アプリケーションが実際に使用するもののみを含める必要があります。
Vite 構成の更新
ビルド プロセスを最適化し、SSR の依存関係を処理するには、Vite 構成を更新します。
// ./server/prod.ts import { Application } from 'express' import fs from 'fs' import path from 'path' import compression from 'compression' import sirv from 'sirv' import { HTML_KEY } from './constants' const CLIENT_PATH = path.resolve(process.cwd(), 'dist/client') const HTML_PATH = path.resolve(process.cwd(), 'dist/client/index.html') const ENTRY_SERVER_PATH = path.resolve(process.cwd(), 'dist/ssr/entry-server.js') export async function setupProd(app: Application) { // Use compression for responses app.use(compression()) // Serve static files from the client build folder app.use(sirv(CLIENT_PATH, { extensions: [] })) app.get('*', async (_, res, next) => { try { // Read the pre-built HTML file let html = fs.readFileSync(HTML_PATH, 'utf-8') // Import the server-side render function and generate HTML const { render } = await import(ENTRY_SERVER_PATH) const appHtml = await render() // Replace the placeholder with the rendered HTML html = html.replace(HTML_KEY, appHtml) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { // Log errors and pass them to the error handler console.error((e as Error).stack) next(e) } }) }
tsconfig.json の更新
tsconfig.json を更新してサーバー ファイルを含め、TypeScript を適切に構成します。
pnpm create vite react-ssr-app --template react-swc-ts
tsup 構成の作成
TypeScript バンドラーである tsup を使用してサーバー コードを構築します。 noExternal オプションは、サーバーにバンドルするパッケージを指定します。 サーバーが使用する追加パッケージを必ず含めてください。
pnpm install
ビルドスクリプトの追加
// ./src/entry-server.tsx import { renderToString } from 'react-dom/server' import App from './App' export function render() { return renderToString(<App />) }
アプリケーションの実行
開発: 次のコマンドを使用して、ホットリロードでアプリケーションを起動します:
// ./src/entry-client.tsx import { hydrateRoot } from 'react-dom/client' import { StrictMode } from 'react' import App from './App' import './index.css' hydrateRoot( document.getElementById('root')!, <StrictMode> <App /> </StrictMode>, )
本番: アプリケーションをビルドし、本番サーバーを起動します:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + React + TS</title> </head> <body> <div> <h3> Create Server </h3> <p>First, install the dependencies:<br> </p> <pre class="brush:php;toolbar:false">pnpm install -D express compression sirv tsup vite-node nodemon @types/express @types/compression
SSR が動作していることを確認するには、サーバーへの最初のネットワーク リクエストを確認します。応答には、アプリケーションの完全にレンダリングされた HTML が含まれている必要があります。
ルーティング
アプリにさまざまなページを追加するには、ルーティングを適切に構成し、クライアントとサーバーの両方のエントリ ポイントで処理する必要があります。
// ./server/index.ts export * from './app'
クライアント側ルーティングの追加
クライアント側のルーティングを有効にするために、クライアント エントリ ポイントで BrowserRouter を使用してアプリケーションをラップします。
// ./server/constants.ts export const NODE_ENV = process.env.NODE_ENV || 'development' export const APP_PORT = process.env.APP_PORT || 3000 export const PROD = NODE_ENV === 'production' export const HTML_KEY = `<!--app-html-->`
サーバー側ルーティングの追加
サーバー側のルーティングを処理するには、サーバーのエントリ ポイントで StaticRouter を使用します。 URL をプロップとして渡し、リクエストに基づいて正しいルートをレンダリングします。
// ./server/app.ts import express from 'express' import { PROD, APP_PORT } from './constants' import { setupProd } from './prod' import { setupDev } from './dev' export async function createServer() { const app = express() if (PROD) { await setupProd(app) } else { await setupDev(app) } app.listen(APP_PORT, () => { console.log(`http://localhost:${APP_PORT}`) }) } createServer()
サーバー構成の更新
開発サーバーと運用サーバーの両方のセットアップを更新して、リクエスト URL をレンダリング関数に渡します。
// ./server/dev.ts import { Application } from 'express' import fs from 'fs' import path from 'path' import { HTML_KEY } from './constants' const HTML_PATH = path.resolve(process.cwd(), 'index.html') const ENTRY_SERVER_PATH = path.resolve(process.cwd(), 'src/entry-server.tsx') export async function setupDev(app: Application) { // Create a Vite development server in middleware mode const vite = await ( await import('vite') ).createServer({ root: process.cwd(), server: { middlewareMode: true }, appType: 'custom', }) // Use Vite middleware for serving files app.use(vite.middlewares) app.get('*', async (req, res, next) => { try { // Read and transform the HTML file let html = fs.readFileSync(HTML_PATH, 'utf-8') html = await vite.transformIndexHtml(req.originalUrl, html) // Load the entry-server.tsx module and render the app const { render } = await vite.ssrLoadModule(ENTRY_SERVER_PATH) const appHtml = await render() // Replace the placeholder with the rendered HTML html = html.replace(HTML_KEY, appHtml) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { // Fix stack traces for Vite and handle errors vite.ssrFixStacktrace(e as Error) console.error((e as Error).stack) next(e) } }) }
これらの変更により、React アプリで SSR と完全に互換性のあるルートを作成できるようになりました。ただし、この基本的なアプローチでは、遅延ロードされたコンポーネント (React.lazy) は処理されません。遅延ロードされたモジュールの管理については、下部にリンクされている私の他の記事 ストリーミングと動的データを使用した高度な React SSR テクニック を参照してください。
ドッカー
アプリケーションをコンテナ化するための Dockerfile は次のとおりです:
// ./server/prod.ts import { Application } from 'express' import fs from 'fs' import path from 'path' import compression from 'compression' import sirv from 'sirv' import { HTML_KEY } from './constants' const CLIENT_PATH = path.resolve(process.cwd(), 'dist/client') const HTML_PATH = path.resolve(process.cwd(), 'dist/client/index.html') const ENTRY_SERVER_PATH = path.resolve(process.cwd(), 'dist/ssr/entry-server.js') export async function setupProd(app: Application) { // Use compression for responses app.use(compression()) // Serve static files from the client build folder app.use(sirv(CLIENT_PATH, { extensions: [] })) app.get('*', async (_, res, next) => { try { // Read the pre-built HTML file let html = fs.readFileSync(HTML_PATH, 'utf-8') // Import the server-side render function and generate HTML const { render } = await import(ENTRY_SERVER_PATH) const appHtml = await render() // Replace the placeholder with the rendered HTML html = html.replace(HTML_KEY, appHtml) res.status(200).set({ 'Content-Type': 'text/html' }).end(html) } catch (e) { // Log errors and pass them to the error handler console.error((e as Error).stack) next(e) } }) }
Docker イメージの構築と実行
// ./vite.config.ts import { defineConfig } from 'vite' import react from '@vitejs/plugin-react-swc' import { dependencies } from './package.json' export default defineConfig(({ mode }) => ({ plugins: [react()], ssr: { noExternal: mode === 'production' ? Object.keys(dependencies) : undefined, }, }))
{ "include": [ "src", "server", "vite.config.ts" ] }
結論
このガイドでは、React を使用して本番環境に対応した SSR アプリケーションを作成するための強力な基盤を確立しました。プロジェクトのセットアップ、ルーティングの構成、Dockerfile の作成方法を学習しました。この設定は、ランディング ページや小さなアプリを効率的に作成するのに最適です。
コードを探索する
- 例: 反応-ssr-基本-例
- テンプレート: 反応-ssr-テンプレート
- Vite Extra テンプレート: template-ssr-react-ts
関連記事
これは、React を使用した SSR に関するシリーズの一部です。他の記事もお楽しみに!
- 本番環境に対応した SSR React アプリケーションの構築 (ここにいます)
- ストリーミングおよび動的データを使用した高度な React SSR テクニック (近日公開予定)
- SSR React アプリケーションでのテーマのセットアップ (近日公開予定)
つながりを保つ
フィードバック、コラボレーション、技術的なアイデアについての議論はいつでも受け付けています。お気軽にご連絡ください。
- ポートフォリオ: maxh1t.xyz
- メール: m4xh17@gmail.com
以上が本番環境に対応した SSR React アプリケーションの構築の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











Pythonは、スムーズな学習曲線と簡潔な構文を備えた初心者により適しています。 JavaScriptは、急な学習曲線と柔軟な構文を備えたフロントエンド開発に適しています。 1。Python構文は直感的で、データサイエンスやバックエンド開発に適しています。 2。JavaScriptは柔軟で、フロントエンドおよびサーバー側のプログラミングで広く使用されています。

C/CからJavaScriptへのシフトには、動的なタイピング、ゴミ収集、非同期プログラミングへの適応が必要です。 1)C/Cは、手動メモリ管理を必要とする静的に型付けられた言語であり、JavaScriptは動的に型付けされ、ごみ収集が自動的に処理されます。 2)C/Cはマシンコードにコンパイルする必要がありますが、JavaScriptは解釈言語です。 3)JavaScriptは、閉鎖、プロトタイプチェーン、約束などの概念を導入します。これにより、柔軟性と非同期プログラミング機能が向上します。

Web開発におけるJavaScriptの主な用途には、クライアントの相互作用、フォーム検証、非同期通信が含まれます。 1)DOM操作による動的なコンテンツの更新とユーザーインタラクション。 2)ユーザーエクスペリエンスを改善するためにデータを提出する前に、クライアントの検証が実行されます。 3)サーバーとのリフレッシュレス通信は、AJAXテクノロジーを通じて達成されます。

現実世界でのJavaScriptのアプリケーションには、フロントエンドとバックエンドの開発が含まれます。 1)DOM操作とイベント処理を含むTODOリストアプリケーションを構築して、フロントエンドアプリケーションを表示します。 2)node.jsを介してRestfulapiを構築し、バックエンドアプリケーションをデモンストレーションします。

JavaScriptエンジンが内部的にどのように機能するかを理解することは、開発者にとってより効率的なコードの作成とパフォーマンスのボトルネックと最適化戦略の理解に役立つためです。 1)エンジンのワークフローには、3つの段階が含まれます。解析、コンパイル、実行。 2)実行プロセス中、エンジンはインラインキャッシュや非表示クラスなどの動的最適化を実行します。 3)ベストプラクティスには、グローバル変数の避け、ループの最適化、constとletsの使用、閉鎖の過度の使用の回避が含まれます。

PythonとJavaScriptには、コミュニティ、ライブラリ、リソースの観点から、独自の利点と短所があります。 1)Pythonコミュニティはフレンドリーで初心者に適していますが、フロントエンドの開発リソースはJavaScriptほど豊富ではありません。 2)Pythonはデータサイエンスおよび機械学習ライブラリで強力ですが、JavaScriptはフロントエンド開発ライブラリとフレームワークで優れています。 3)どちらも豊富な学習リソースを持っていますが、Pythonは公式文書から始めるのに適していますが、JavaScriptはMDNWebDocsにより優れています。選択は、プロジェクトのニーズと個人的な関心に基づいている必要があります。

開発環境におけるPythonとJavaScriptの両方の選択が重要です。 1)Pythonの開発環境には、Pycharm、Jupyternotebook、Anacondaが含まれます。これらは、データサイエンスと迅速なプロトタイピングに適しています。 2)JavaScriptの開発環境には、フロントエンドおよびバックエンド開発に適したnode.js、vscode、およびwebpackが含まれます。プロジェクトのニーズに応じて適切なツールを選択すると、開発効率とプロジェクトの成功率が向上する可能性があります。

CとCは、主に通訳者とJITコンパイラを実装するために使用されるJavaScriptエンジンで重要な役割を果たします。 1)cは、JavaScriptソースコードを解析し、抽象的な構文ツリーを生成するために使用されます。 2)Cは、Bytecodeの生成と実行を担当します。 3)Cは、JITコンパイラを実装し、実行時にホットスポットコードを最適化およびコンパイルし、JavaScriptの実行効率を大幅に改善します。
