Heim >Web-Frontend >js-Tutorial >Ein einfacher Ansatz für SSR mit React und esbuild
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:
Hinweis: Den Inhalt dieses Tutorials finden Sie in meinem Repo https://github.com/willyelm/react-app
Als Voraussetzung für unser Setup benötigen wir die Installation von Node.js und die folgenden Pakete:
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:
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" ] }
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.
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" ] }
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();
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:
Den Quellcode dieses Tutorials finden Sie auch in meinem Repo willyelm/react-app
Dieses Projekt nutzt die neuesten Funktionen von React@19, React-Router-Dom@7 und anderen, um SSR zu konfigurieren.
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…
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:
Vielen Dank fürs Lesen und viel Spaß beim Codieren.
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!