Heim >Web-Frontend >js-Tutorial >Erstellen produktionsbereiter SSR-Reaktionsanwendungen

Erstellen produktionsbereiter SSR-Reaktionsanwendungen

Mary-Kate Olsen
Mary-Kate OlsenOriginal
2025-01-05 11:51:40144Durchsuche

Building Production-Ready SSR React Applications

In einer Welt, in der jede Millisekunde zählt, ist serverseitiges Rendering zu einer wesentlichen Funktion für Frontend-Anwendungen geworden.

Dieser Leitfaden führt Sie durch die grundlegenden Muster zum Aufbau eines produktionsbereiten SSR mit React. Sie erhalten ein Verständnis für die Prinzipien hinter React-basierten Frameworks mit integriertem SSR (wie Next.js) und lernen, wie Sie Ihre eigenen benutzerdefinierten Lösungen erstellen.

Der bereitgestellte Code ist produktionsbereit und umfasst einen vollständigen Build-Prozess für Client- und Serverteile, einschließlich einer Docker-Datei. In dieser Implementierung wird Vite zum Erstellen des Client- und SSR-Codes verwendet, Sie können jedoch auch andere Tools Ihrer Wahl verwenden. Vite ermöglicht dem Client auch Hot-Reloading während des Entwicklungsmodus.

Wenn Sie an einer Version dieses Setups ohne Vite interessiert sind, können Sie sich gerne an uns wenden.

Inhaltsverzeichnis

  • Was ist SSR?
  • Erstellen der App
    • Vite wird initialisiert
    • React-Komponenten aktualisieren
    • Server erstellen
    • Konfigurieren des Builds
  • Routing
  • Docker
  • Fazit

Was ist SSR?

Serverseitiges Rendering (SSR) ist eine Technik in der Webentwicklung, bei der der Server den HTML-Inhalt einer Webseite generiert, bevor er ihn an den Browser sendet. Im Gegensatz zum herkömmlichen clientseitigen Rendering (CSR), bei dem JavaScript den Inhalt auf dem Gerät des Benutzers erstellt, nachdem eine leere HTML-Shell geladen wurde, liefert SSR vollständig gerendertes HTML direkt vom Server.

Hauptvorteile von SSR:

  • Verbesserte SEO: Da Suchmaschinen-Crawler vollständig gerenderte Inhalte erhalten, sorgt SSR für eine bessere Indexierung und Rangliste.
  • Schnellerer erster Paint: Benutzer sehen aussagekräftige Inhalte fast sofort, da der Server die schwere Arbeit übernimmt Rendering.
  • Verbesserte Leistung: Durch die Reduzierung der Rendering-Arbeitslast im Browser sorgt SSR für ein reibungsloseres Erlebnis Benutzer auf älteren oder weniger leistungsstarken Geräten.
  • Nahtlose Server-zu-Client-Datenübertragung: SSR ermöglicht Ihnen die Weitergabe dynamischer serverseitiger Daten an den Client ohne Neuaufbau des Client-Bundles.

Erstellen der App

Der Ablauf Ihrer App mit SSR folgt diesen Schritten:

  1. Lesen Sie die HTML-Vorlagendatei.
  2. React initialisieren und eine HTML-Zeichenfolge des App-Inhalts generieren.
  3. Fügen Sie die generierte HTML-Zeichenfolge in die Vorlage ein.
  4. Senden Sie den vollständigen HTML-Code an den Browser.
  5. Passen Sie auf dem Client die HTML-Tags an und hydrieren Sie die Anwendung, sodass sie interaktiv wird.

Vite wird initialisiert

Ich bevorzuge die Vite-Vorlage „pnpm“ und „react-swc-ts“, aber Sie können jedes andere Setup wählen.

pnpm create vite react-ssr-app --template react-swc-ts

Installieren Sie die Abhängigkeiten:

pnpm create vite react-ssr-app --template react-swc-ts

Aktualisieren von Reaktionskomponenten

In einer typischen React-Anwendung gibt es einen einzigen main.tsx-Einstiegspunkt für index.html. Bei SSR benötigen Sie zwei Einstiegspunkte: einen für den Server und einen für den Client.

Server-Einstiegspunkt

Der Node.js-Server führt Ihre App aus und generiert den HTML-Code, indem er Ihre React-Komponenten in einen String (renderToString) rendert.

pnpm install

Client-Einstiegspunkt

Der Browser hydratisiert das vom Server generierte HTML und verbindet es mit dem JavaScript, um die Seite interaktiv zu machen.

Hydration ist der Prozess des Anhängens von Ereignis-Listenern und anderen dynamischen Verhaltensweisen an den vom Server gerenderten statischen HTML-Code.

// ./src/entry-server.tsx
import { renderToString } from 'react-dom/server'
import App from './App'

export function render() {
  return renderToString(<App />)
}

Index.html wird aktualisiert

Aktualisieren Sie die Datei index.html im Stammverzeichnis Ihres Projekts. Das Der Platzhalter ist der Ort, an dem der Server den generierten HTML-Code einfügt.

// ./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>,
)

Alle für den Server erforderlichen Abhängigkeiten sollten als Entwicklungsabhängigkeiten (devDependencies) installiert werden, um sicherzustellen, dass sie nicht im Client-Bundle enthalten sind.

Als nächstes erstellen Sie im Stammverzeichnis Ihres Projekts einen Ordner mit dem Namen ./server und fügen die folgenden Dateien hinzu.

Erneutes Exportieren der Hauptserverdatei

Exportieren Sie die Hauptserverdatei erneut. Dadurch wird das Ausführen von Befehlen komfortabler.

<!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

Konstanten definieren

Die HTML_KEY-Konstante muss mit dem Platzhalterkommentar in index.html übereinstimmen. Andere Konstanten verwalten Umgebungseinstellungen.

// ./server/index.ts
export * from './app'

Erstellen des Express-Servers

Richten Sie einen Express-Server mit unterschiedlichen Konfigurationen für Entwicklungs- und Produktionsumgebungen ein.

// ./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-->`

Konfiguration des Entwicklungsmodus

Verwenden Sie in der Entwicklung die Middleware von Vite, um Anfragen zu verarbeiten und die Datei index.html durch Hot-Reload dynamisch umzuwandeln. Der Server lädt die React-Anwendung und rendert sie bei jeder Anfrage in 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()

Konfiguration des Produktionsmodus

Verwenden Sie in der Produktion die Komprimierung, um die Leistung zu optimieren, sirv, um statische Dateien bereitzustellen, und das vorgefertigte Serverpaket, um die App zu rendern.

// ./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)
    }
  })
}

Konfigurieren des Builds

Um die Best Practices für die Erstellung Ihrer Anwendung zu befolgen, sollten Sie alle unnötigen Pakete ausschließen und nur das einschließen, was Ihre Anwendung tatsächlich verwendet.

Aktualisieren der Vite-Konfiguration

Aktualisieren Sie Ihre Vite-Konfiguration, um den Build-Prozess zu optimieren und SSR-Abhängigkeiten zu verarbeiten:

// ./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)
    }
  })
}

Aktualisierung von tsconfig.json

Aktualisieren Sie Ihre tsconfig.json, um die Serverdateien einzuschließen, und konfigurieren Sie TypeScript entsprechend:

pnpm create vite react-ssr-app --template react-swc-ts

Tsup-Konfiguration erstellen

Verwenden Sie tsup, einen TypeScript-Bundler, um den Servercode zu erstellen. Die Option noExternal gibt die Pakete an, die mit dem Server gebündelt werden sollen. Stellen Sie sicher, dass Sie alle zusätzlichen Pakete einschließen, die Ihr Server verwendet.

pnpm install

Build-Skripte hinzufügen

// ./src/entry-server.tsx
import { renderToString } from 'react-dom/server'
import App from './App'

export function render() {
  return renderToString(<App />)
}

Ausführen der Anwendung

Entwicklung: Verwenden Sie den folgenden Befehl, um die Anwendung mit Hot-Neuladen zu starten:

// ./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>,
)

Produktion: Erstellen Sie die Anwendung und starten Sie den Produktionsserver:

<!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

Um zu überprüfen, ob SSR funktioniert, überprüfen Sie die erste Netzwerkanfrage an Ihren Server. Die Antwort sollte den vollständig gerenderten HTML-Code Ihrer Anwendung enthalten.

Routenführung

Um Ihrer App verschiedene Seiten hinzuzufügen, müssen Sie das Routing richtig konfigurieren und es sowohl in Client- als auch in Server-Einstiegspunkten verarbeiten.

// ./server/index.ts
export * from './app'

Clientseitiges Routing hinzufügen

Umschließen Sie Ihre Anwendung mit BrowserRouter im Client-Einstiegspunkt, um clientseitiges Routing zu ermöglichen.

// ./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-->`

Serverseitiges Routing hinzufügen

Verwenden Sie StaticRouter im Server-Einstiegspunkt, um das serverseitige Routing zu verwalten. Übergeben Sie die URL als Requisite, um die richtige Route basierend auf der Anfrage darzustellen.

// ./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()

Aktualisieren von Serverkonfigurationen

Aktualisieren Sie sowohl Ihre Entwicklungs- als auch Ihre Produktionsserver-Setups, um die Anforderungs-URL an die Renderfunktion zu übergeben:

// ./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)
    }
  })
}

Mit diesen Änderungen können Sie jetzt Routen in Ihrer React-App erstellen, die vollständig mit SSR kompatibel sind. Dieser grundlegende Ansatz behandelt jedoch keine verzögert geladenen Komponenten (React.lazy). Informationen zur Verwaltung verzögert geladener Module finden Sie in meinem anderen Artikel, Erweiterte React SSR-Techniken mit Streaming und dynamischen Daten, der unten verlinkt ist.

Docker

Hier ist eine Docker-Datei zum Containerisieren Ihrer Anwendung:

// ./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)
    }
  })
}

Erstellen und Ausführen des Docker-Images

// ./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"
  ]
}

Abschluss

In diesem Leitfaden haben wir eine solide Grundlage für die Erstellung produktionsbereiter SSR-Anwendungen mit React geschaffen. Sie haben gelernt, wie Sie das Projekt einrichten, das Routing konfigurieren und eine Docker-Datei erstellen. Dieses Setup ist ideal für die effiziente Erstellung von Landingpages oder kleinen Apps.

Entdecken Sie den Kodex

  • Beispiel: React-SSR-Basics-Example
  • Vorlage: React-SSR-Template
  • Vite Extra Template: template-ssr-react-ts

Verwandte Artikel

Dies ist Teil meiner Serie über SSR mit React. Bleiben Sie dran für weitere Artikel!

  • Produktionsreife SSR-React-Anwendungen erstellen (Sie sind hier)
  • Erweiterte React SSR-Techniken mit Streaming und dynamischen Daten (in Kürze erhältlich)
  • Themen in SSR React-Anwendungen einrichten (in Kürze verfügbar)

Bleiben Sie in Verbindung

Ich bin immer offen für Feedback, Zusammenarbeit oder die Diskussion technischer Ideen – zögern Sie nicht, mich zu kontaktieren!

  • Portfolio: maxh1t.xyz
  • E-Mail: m4xh17@gmail.com

Das obige ist der detaillierte Inhalt vonErstellen produktionsbereiter SSR-Reaktionsanwendungen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn