Heim >Web-Frontend >js-Tutorial >Ein einfacher Ansatz für SSR mit React und esbuild

Ein einfacher Ansatz für SSR mit React und esbuild

Barbara Streisand
Barbara StreisandOriginal
2024-12-30 20:56:14657Durchsuche

In diesem Blog wird untersucht, wie Sie durch die Nutzung der neuesten Funktionen von React eine leichte, flexible React-Anwendung mit minimalen Tools und Frameworks erstellen. Während Frameworks wie Next.js und Remix für viele Anwendungsfälle hervorragend geeignet sind, richtet sich dieser Leitfaden an diejenigen, die die volle Kontrolle haben und gleichzeitig ähnliche Funktionen beibehalten möchten.

Mit der Veröffentlichung von React 19 und seinen neuen Funktionen zur Unterstützung von SSR habe ich beschlossen, mit der Erstellung einer React-Anwendung zu experimentieren, die SSR mit minimalem Werkzeugaufwand unterstützt. Lassen Sie uns jedoch zunächst die wesentlichen Funktionen erkunden, die Next.js bietet und die wir berücksichtigen müssen:

Feature Next.js
SSR (Server-Side Rendering) Built-in with minimal setup.
SSG (Static Site Generation) Built-in with getStaticProps.
Routing File-based routing.
Code Splitting Automatic.
Image Optimization Built-in with next/image.
Performance Optimizations Automatic (e.g., prefetching, critical CSS).
SEO Built-in tools like next/head.

Basierend auf diesen Funktionen werden wir ein minimales Setup erstellen; Hier ist die Schritt-für-Schritt-Anleitung:

Inhaltsverzeichnis

  • Einrichtung
  • esbuild konfigurieren
  • Express-App und Routing
  • React App
  • Ausführen
  • Nächste Schritte
  • Referenzen

Hinweis: Den Inhalt dieses Tutorials finden Sie in meinem Repo https://github.com/willyelm/react-app

Aufstellen

Als Voraussetzung für unser Setup benötigen wir die Installation von Node.js und die folgenden Pakete:

  • reagieren: Komponentenbasierte und interaktive UI-Bibliothek.
  • react-dom: Hydratiert SSR-Inhalte mit React-Funktionalität.
  • react-router-dom: Routen mit React verarbeiten.
  • Express: Node.js einfacher Server für statische und REST-APIs.
  • esbuild: TypeScript transpilieren und JS, CSS, SVG und andere Dateien bündeln.
  • Typoskript: Hinzufügen von Typing zu unserem Quellcode.

Unser Setup übernimmt das Routing mit Express, um statische Dateien, öffentliche Dateien und REST-APIs bereitzustellen. Behandeln Sie dann alle Anfragen mit React-Router-Dom. Sobald es in den Browser geladen ist, versorgt unser Client-Bundle unsere vorgerenderten Inhalte mit Feuchtigkeit und macht unsere Komponenten interaktiv. Hier ist eine schematische Darstellung dieser Idee:

A Simple Approach to SSR with React and esbuild

Das Diagramm veranschaulicht, wie die Express-App Anfragen verarbeitet, indem sie React-Komponenten auf dem Server vorab rendert und HTML zurückgibt. Auf der Clientseite hydratisiert React diese vorgerenderten Komponenten, um Interaktivität zu ermöglichen.

Mit diesem Diagramm im Hinterkopf erstellen wir eine Ordnerstruktur:

react-app/             # This will be our workspace directory.
  - public/            
  - scripts/           
    - build.js         # Bundle our server and client scripts.
    - config.js        # esbuild config to bundle.
    - dev.js           # Bundle on watch mode and run server.
  - src/
    - App/             # Our components will be here.
      - App.tsx        # The main application with browser routing.
      - Home.tsx.      # Our default page component.
      - NotFound.tsx   # Fallback page for unmatched routes.
    - index.tsx        # Hydrate our pre-rendered client app.
    - main.tsx         # Server app with SSR components.
    - style.css        # Initial stylesheet.   
  package.json
  tsconfig.json

Lassen Sie uns unsere Abhängigkeiten hinzufügen und unsere package.json einrichten:

{
  "name": "react-app",
  "type": "module",
  "devDependencies": {
    "@types/express": "^5.0.0",
    "@types/node": "^22.10.2",
    "@types/react": "^19.0.2",
    "@types/react-dom": "^19.0.2",
    "esbuild": "^0.24.2",
    "typescript": "^5.7.2"
  },
  "dependencies": {
    "express": "^4.21.2",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-router-dom": "^7.1.0"
  },
  "scripts": {
    "build": "node scripts/build",
    "dev": "node scripts/dev",
    "start": "node dist/main.js"
  }
}

Hinweis: Die Eigenschaft „type“: „module“ ist erforderlich, damit node.js ESM-Skripte ausführen kann.

Da wir Typescript verwenden, konfigurieren wir eine tsconfig.json-Datei:

{
  "compilerOptions": {
    "esModuleInterop": true,
    "verbatimModuleSyntax": true,
    "noEmit": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "strict": true,
    "lib": ["DOM", "DOM.Iterable", "ES2022"],
    "target": "ES2022",
    "module": "ES2022",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "baseUrl": ".",
    "paths": {
      "src": [
        "./src/"
      ]
    }
  },
  "include": [
    "src"
  ],
  "exclude": [
    "node_modules"
  ]
}

Esbuild konfigurieren

Warum esbuild? Im Vergleich zu anderen Tools hält esbuild die Dinge auf ein Minimum, ist sehr schnell (der derzeit schnellste Bundler) und unterstützt standardmäßig TypeScript und ESM.

In diesem Setup verwenden wir esbuild, um unsere Entwicklungs- und Build-Skripte zu erstellen und sowohl Client- als auch Server-Bundles zu transpilieren. In diesem Abschnitt arbeiten wir im Skriptordner unseres Arbeitsbereichs.

scripts/config.js: Diese Datei enthält eine Basiskonfiguration für das Client- und Server-Bundle, die für unsere Skripte gemeinsam genutzt wird.

import path from 'node:path';
// Working dir
const workspace = process.cwd();
// Server bundle configuration
export const serverConfig = {
  bundle: true,
  platform: 'node', 
  format: 'esm',        // Support esm packages
  packages: 'external', // Omit node packages from our node bundle
  logLevel: 'error',
  sourcemap: 'external',
  entryPoints: {
    main: path.join(workspace, 'src', 'main.tsx') // Express app
  },
  tsconfig: path.join(workspace, 'tsconfig.json'),
  outdir: path.join(workspace, 'dist')
};

// Client bundle configuration
export const clientConfig = {
  bundle: true,
  platform: 'browser',
  format: 'esm',
  sourcemap: 'external',
  logLevel: 'error',
  tsconfig: path.join(workspace, 'tsconfig.json'),
  entryPoints: {
    index: path.join(workspace, 'src', 'index.tsx'), // Client react app
    style: path.join(workspace, 'src', 'style.css')  // Stylesheet
  },
  outdir: path.join(workspace, 'dist', 'static'),    // Served as /static by express
};

scripts/dev.js: Dieses Skript bündelt sowohl die Client- als auch die Server-App und führt das Hauptserverskript im Überwachungsmodus aus.

react-app/             # This will be our workspace directory.
  - public/            
  - scripts/           
    - build.js         # Bundle our server and client scripts.
    - config.js        # esbuild config to bundle.
    - dev.js           # Bundle on watch mode and run server.
  - src/
    - App/             # Our components will be here.
      - App.tsx        # The main application with browser routing.
      - Home.tsx.      # Our default page component.
      - NotFound.tsx   # Fallback page for unmatched routes.
    - index.tsx        # Hydrate our pre-rendered client app.
    - main.tsx         # Server app with SSR components.
    - style.css        # Initial stylesheet.   
  package.json
  tsconfig.json

Mit diesem Skript sollten wir in der Lage sein, npm run dev wie in package.json konfiguriert in unserem Arbeitsbereich auszuführen.

scripts/build.js: Ähnlich wie dev, aber wir müssen nur Minify aktivieren.

{
  "name": "react-app",
  "type": "module",
  "devDependencies": {
    "@types/express": "^5.0.0",
    "@types/node": "^22.10.2",
    "@types/react": "^19.0.2",
    "@types/react-dom": "^19.0.2",
    "esbuild": "^0.24.2",
    "typescript": "^5.7.2"
  },
  "dependencies": {
    "express": "^4.21.2",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-router-dom": "^7.1.0"
  },
  "scripts": {
    "build": "node scripts/build",
    "dev": "node scripts/dev",
    "start": "node dist/main.js"
  }
}

Dieses Skript generiert unser dist-Bundle, das für die Produktion bereit ist, indem es npm run build ausführt und die App mit npm start ausführt.

Nachdem wir esbuild so konfiguriert haben, dass es sowohl unseren Knoten als auch unsere Client-App bündelt, beginnen wir mit der Erstellung eines Express-Servers und der Implementierung von React SSR.

Express-App und Routing

Dies ist eine einfache Anwendung, die den Express-Statik- und Middleware-Ansatz verwendet, um statische Dateien bereitzustellen, Serverrouten zu verwalten und mithilfe von React-Router-Dom weiterzuleiten.

src/main.tsx: Dies ist die Hauptanwendung von Node.js, die den Server initialisiert, Routen mit Express verarbeitet und React SSR implementiert.

{
  "compilerOptions": {
    "esModuleInterop": true,
    "verbatimModuleSyntax": true,
    "noEmit": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "strict": true,
    "lib": ["DOM", "DOM.Iterable", "ES2022"],
    "target": "ES2022",
    "module": "ES2022",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "baseUrl": ".",
    "paths": {
      "src": [
        "./src/"
      ]
    }
  },
  "include": [
    "src"
  ],
  "exclude": [
    "node_modules"
  ]
}

Reaktions-App

Die React-App verarbeitet die Routen mithilfe von React-Router-Dom. Unsere App wird aus einer Startseite und einer NotFound-Seite zum Testen der Flüssigkeitszufuhr bestehen. Wir werden auf der Startseite eine Zählerschaltfläche hinzufügen und die Vorteile von React 19 nutzen Aktualisieren Sie den Titel und die Beschreibung der Meta-Tags.

src/App/Home.tsx: Eine sehr minimale Funktionskomponente.

import path from 'node:path';
// Working dir
const workspace = process.cwd();
// Server bundle configuration
export const serverConfig = {
  bundle: true,
  platform: 'node', 
  format: 'esm',        // Support esm packages
  packages: 'external', // Omit node packages from our node bundle
  logLevel: 'error',
  sourcemap: 'external',
  entryPoints: {
    main: path.join(workspace, 'src', 'main.tsx') // Express app
  },
  tsconfig: path.join(workspace, 'tsconfig.json'),
  outdir: path.join(workspace, 'dist')
};

// Client bundle configuration
export const clientConfig = {
  bundle: true,
  platform: 'browser',
  format: 'esm',
  sourcemap: 'external',
  logLevel: 'error',
  tsconfig: path.join(workspace, 'tsconfig.json'),
  entryPoints: {
    index: path.join(workspace, 'src', 'index.tsx'), // Client react app
    style: path.join(workspace, 'src', 'style.css')  // Stylesheet
  },
  outdir: path.join(workspace, 'dist', 'static'),    // Served as /static by express
};

src/App/NotFound.tsx: Standard-Funktionskomponente, wenn die Seite nicht gefunden wird.

import { spawn } from 'node:child_process';
import path from 'node:path';
import { context } from 'esbuild';
import { serverConfig, clientConfig } from './config.js';
// Working dir
const workspace = process.cwd();
// Dev process
async function dev() {
  // Build server in watch mode
  const serverContext = await context(serverConfig);
  serverContext.watch();
  // Build client in watch mode
  const clientContext = await context(clientConfig);
  clientContext.watch();
  // Run server
  const childProcess = spawn('node', [
    '--watch',
    path.join(workspace, 'dist', 'main.js')
  ], {
    stdio: 'inherit'
  });
  // Kill child process on program interruption
  process.on('SIGINT', () => {
    if (childProcess) {
      childProcess.kill();
    }
    process.exit(0);
  });
}
// Start the dev process
dev();

src/App/App.tsx: Einrichten unserer App mit React-Router-Dom.

import { build } from 'esbuild';
import { clientConfig, serverConfig } from './config.js';
// build process
async function bundle() {
  // Build server
  await build({
    ...serverConfig,
    minify: true
  });
  // Build client
  await build({
    ...clientConfig,
    minify: true
  });
}
// Start the build process
bundle();

Laufen

Schließlich sind die Esbuild-Skripte konfiguriert, der Express-Server eingerichtet und die SSR-Reaktion implementiert. Wir können unseren Server betreiben:

import path from 'node:path';
import express, { type Request, type Response } from 'express';
import { renderToPipeableStream } from 'react-dom/server';
import { StaticRouter } from 'react-router';
import { App } from './App/App';

const app = express(); // Create Express App
const port = 3000; // Port to listen
const workspace = process.cwd(); // workspace
// Serve static files like js bundles and css files
app.use('/static', express.static(path.join(workspace, 'dist', 'static')));
// Server files from the /public folder
app.use(express.static(path.join(workspace, 'public')));
// Fallback to render the SSR react app
app.use((request: Request, response: Response) => {
  // React SSR rendering as a stream
  const { pipe } = renderToPipeableStream(
    <html lang="en">
      <head>
        <meta charSet="UTF-8" />
        <link rel='stylesheet' href={`/static/style.css`} />
      </head>
      <body>
        <base href="/" />
        <div>


<p>src/index.tsx: On the client side to activate our components and make them interactive we need to "hydrate".<br>
</p>
<pre class="brush:php;toolbar:false">import { hydrateRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router';
import { App } from './App/App';
// Hydrate pre-renderer #app element
hydrateRoot(
  document.getElementById('app') as HTMLElement,
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

Dadurch wird eine Meldung angezeigt, dass [App] Port 3000 überwacht. Navigieren Sie zu
http://localhost:3000, um es auszuprobieren.

SSR- und SEO-Meta-Tags testen:

A Simple Approach to SSR with React and esbuild

Den Quellcode dieses Tutorials finden Sie auch in meinem Repo willyelm/react-app

A Simple Approach to SSR with React and esbuild willyelm / React-App

Ein einfaches React-App-Setup mit SSR und Esbuild

Dieses Projekt nutzt die neuesten Funktionen von React@19, React-Router-Dom@7 und anderen, um SSR zu konfigurieren.

Abhängigkeiten

  • reagieren: Komponentenbasierte und interaktive UI-Bibliothek.
  • react-dom: Hydratiert SSR-Inhalte mit React-Funktionalität.
  • react-router-dom: Routen mit React verarbeiten.
  • Express: Node.js einfacher Server für statische und REST-APIs.
  • esbuild: TypeScript transilieren und JS, CSS, SVG und andere Dateien bündeln.
  • Typoskript: Hinzufügen von Typing zu unserem Quellcode.

Projektstruktur

react-app/             # This will be our workspace directory.
  - public/            
  - scripts/           
    - build.js         # Bundle our server and client scripts.
    - config.js        # esbuild config to bundle.
    - dev.js           # Bundle on watch mode and run server.
  - src/
    - App/             # Our components will be here.
      - App.tsx        # The main application with browser routing.
      - Home.tsx.      # Our default page component.
      - NotFound.tsx   # Fallback page for unmatched routes.
    - index.tsx        # Hydrate our pre-rendered client app.
    - main.tsx         # Server app with SSR components.
    - style.css        # Initial stylesheet.   
  package.json
  tsconfig.json
Vollbildmodus aufrufen Vollbildmodus verlassen
Auf GitHub ansehen

Nächste Schritte

In diesem Leitfaden haben wir eine React-App mit SSR unter Verwendung von Esbuild und Express erstellt und uns dabei auf ein minimales und flexibles Setup konzentriert. Durch den Einsatz moderner Tools wie React 19, React Router DOM 7 und esbuild haben wir einen schnellen und effizienten Workflow erreicht und gleichzeitig den Overhead größerer Frameworks vermieden.

Wir können diese Implementierung um Folgendes erweitern:

  • TailwindCSS: Konfigurieren Sie PostCSS und esbuild, um TailwindCSS für das Styling zu unterstützen.
  • Jest: Unit-Tests mit Jest und @testing-library hinzufügen.
  • Playwright: Konfigurieren Sie End-to-End-Tests mit Playwright.
  • SSG: Implementieren Sie SSG (statische Site-Generierung), um die Leistung und Serverauslastung zu verbessern.
  • HMR: Unterstützen Sie HMR (Hot Module Replacement) mit esbuild im Entwicklungsmodus, um die Effizienz zu steigern.

Vielen Dank fürs Lesen und viel Spaß beim Codieren.

Referenzen

  • Reagieren Sie 19
  • React Router DOM
  • SSR reagieren
  • Express.js
  • TypeScript
  • esbuild

Das obige ist der detaillierte Inhalt vonEin einfacher Ansatz für SSR mit React und esbuild. 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