Maison >interface Web >js tutoriel >Création d'applications SSR React prêtes pour la production

Création d'applications SSR React prêtes pour la production

Mary-Kate Olsen
Mary-Kate Olsenoriginal
2025-01-05 11:51:40144parcourir

Building Production-Ready SSR React Applications

Dans un monde où chaque milliseconde compte, le rendu côté serveur est devenu une fonctionnalité essentielle pour les applications frontend.

Ce guide vous guidera à travers les modèles fondamentaux pour créer un SSR prêt pour la production avec React. Vous comprendrez les principes qui sous-tendent les frameworks basés sur React avec SSR intégré (comme Next.js) et apprendrez à créer vos propres solutions personnalisées.

Le code fourni est prêt pour la production, avec un processus de construction complet pour les parties client et serveur, y compris un Dockerfile. Dans cette implémentation, Vite est utilisé pour créer le code client et SSR, mais vous pouvez utiliser tout autre outil de votre choix. Vite permet également le rechargement à chaud pendant le mode développement pour le client.

Si vous êtes intéressé par une version de cette configuration sans Vite, n'hésitez pas à nous contacter.

Table des matières

  • Qu'est-ce que la RSS
  • Création de l'application
    • Initialisation de Vite
    • Mise à jour des composants React
    • Créer un serveur
    • Configurer la build
  • Routage
  • Docker
  • Conclusion

Qu'est-ce que la RSS

Le rendu côté serveur (SSR) est une technique de développement Web où le serveur génère le contenu HTML d'une page Web avant de l'envoyer au navigateur. Contrairement au rendu côté client (CSR) traditionnel, où JavaScript crée le contenu sur l'appareil de l'utilisateur après avoir chargé un shell HTML vide, SSR fournit un HTML entièrement rendu directement depuis le serveur.

Principaux avantages de la RSS :

  • Référencement amélioré : étant donné que les robots des moteurs de recherche reçoivent un contenu entièrement rendu, SSR garantit une meilleure indexation et classement.
  • Faster First Paint : les utilisateurs voient un contenu significatif presque immédiatement, car le serveur gère le gros du travail. rendu.
  • Performances améliorées : en réduisant la charge de travail de rendu sur le navigateur, SSR offre une expérience plus fluide pour utilisateurs sur des appareils plus anciens ou moins puissants.
  • Transfert de données transparent du serveur au client : SSR vous permet de transmettre des données dynamiques côté serveur au client sans reconstruire le bundle client.

Création de l'application

Le flux de votre application avec SSR suit ces étapes :

  1. Lisez le fichier HTML du modèle.
  2. Initialisez React et générez une chaîne HTML du contenu de l'application.
  3. Injectez la chaîne HTML générée dans le modèle.
  4. Envoyez le code HTML complet au navigateur.
  5. Sur le client, faites correspondre les balises HTML et hydratez l'application, la rendant interactive.

Initialisation de Vite

Je préfère utiliser les modèles Pnpm et React-swc-ts Vite, mais vous pouvez choisir n'importe quelle autre configuration.

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

Installer les dépendances :

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

Mise à jour des composants React

Dans une application React typique, il existe un seul point d'entrée main.tsx pour index.html. Avec SSR, vous avez besoin de deux points d'entrée : un pour le serveur et un pour le client.

Point d'entrée du serveur

Le serveur Node.js exécutera votre application et générera le HTML en rendant vos composants React sous forme de chaîne (renderToString).

pnpm install

Point d'entrée client

Le navigateur hydratera le HTML généré par le serveur, en le connectant au JavaScript pour rendre la page interactive.

L'hydratation est le processus d'attachement d'écouteurs d'événements et d'autres comportements dynamiques au HTML statique rendu par le serveur.

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

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

Mise à jour d'index.html

Mettez à jour le fichier index.html à la racine de votre projet. Le l'espace réservé est l'endroit où le serveur injectera le HTML généré.

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

Toutes les dépendances requises pour le serveur doivent être installées en tant que dépendances de développement (devDependencies) pour garantir qu'elles ne sont pas incluses dans le bundle client.

Ensuite, créez un dossier à la racine de votre projet nommé ./server et ajoutez les fichiers suivants.

Réexporter le fichier du serveur principal

Réexportez le fichier du serveur principal. Cela rend l'exécution de commandes plus pratique.

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

Définir des constantes

La constante HTML_KEY doit correspondre au commentaire d'espace réservé dans index.html. D'autres constantes gèrent les paramètres d'environnement.

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

Création du serveur express

Configurez un serveur Express avec différentes configurations pour les environnements de développement et de production.

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

Configuration du mode développement

En développement, utilisez le middleware de Vite pour gérer les requêtes et transformer dynamiquement le fichier index.html avec rechargement à chaud. Le serveur chargera l'application React et la restituera au format HTML à chaque requête.

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

Configuration du mode production

En production, utilisez la compression pour optimiser les performances, sirv pour servir les fichiers statiques et le bundle de serveurs prédéfini pour restituer l'application.

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

Configuration de la construction

Pour suivre les meilleures pratiques de création de votre application, vous devez exclure tous les packages inutiles et inclure uniquement ce que votre application utilise réellement.

Mise à jour de la configuration de Vite

Mettez à jour votre configuration Vite pour optimiser le processus de build et gérer les dépendances SSR :

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

Mise à jour de tsconfig.json

Mettez à jour votre tsconfig.json pour inclure les fichiers du serveur et configurez TypeScript de manière appropriée :

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

Création d'une configuration tsup

Utilisez tsup, un bundler TypeScript, pour créer le code du serveur. L'option noExternal spécifie les packages à regrouper avec le serveur. Assurez-vous d'inclure tous les packages supplémentaires utilisés par votre serveur.

pnpm install

Ajout de scripts de construction

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

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

Exécution de l'application

Développement : Utilisez la commande suivante pour démarrer l'application avec rechargement à chaud :

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

Production : Créez l'application et démarrez le serveur de production :

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

Pour vérifier que SSR fonctionne, vérifiez la première requête réseau adressée à votre serveur. La réponse doit contenir le HTML entièrement rendu de votre application.

Routage

Pour ajouter différentes pages à votre application, vous devez configurer correctement le routage et le gérer dans les points d'entrée client et serveur.

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

Ajout du routage côté client

Enveloppez votre application avec BrowserRouter dans le point d'entrée client pour activer le routage côté client.

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

Ajout du routage côté serveur

Utilisez StaticRouter dans le point d'entrée du serveur pour gérer le routage côté serveur. Transmettez l'URL comme accessoire pour afficher l'itinéraire correct en fonction de la demande.

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

Mise à jour des configurations du serveur

Mettez à jour les configurations de votre serveur de développement et de production pour transmettre l'URL de la requête à la fonction de rendu :

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

Avec ces modifications, vous pouvez désormais créer des itinéraires dans votre application React entièrement compatibles avec SSR. Cependant, cette approche de base ne gère pas les composants chargés paresseux (React.lazy). Pour gérer les modules chargés paresseux, veuillez vous référer à mon autre article, Techniques React SSR avancées avec streaming et données dynamiques, lié en bas.

Docker

Voici un Dockerfile pour conteneuriser votre application :

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

Créer et exécuter l'image 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"
  ]
}

Conclusion

Dans ce guide, nous avons établi une base solide pour créer des applications SSR prêtes pour la production avec React. Vous avez appris à configurer le projet, à configurer le routage et à créer un Dockerfile. Cette configuration est idéale pour créer efficacement des pages de destination ou des petites applications.

Explorez le code

  • Exemple : react-ssr-basics-example
  • Modèle : react-ssr-template
  • Modèle supplémentaire Vite : template-ssr-react-ts

Articles connexes

Cela fait partie de ma série sur la RSS avec React. Restez à l'écoute pour plus d'articles !

  • Création d'applications SSR React prêtes pour la production (Vous êtes ici)
  • Techniques React SSR avancées avec streaming et données dynamiques (à venir)
  • Configuration de thèmes dans les applications SSR React (à venir)

Restez connecté

Je suis toujours ouvert aux commentaires, à la collaboration ou aux discussions sur des idées techniques – n'hésitez pas à nous contacter !

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn