Angular est-il vraiment plus compliqué que React ?

Quand on débute avec Angular, il est facile de se sentir découragé face à la multitude de concepts à assimiler. Cette complexité peut inciter à se tourner vers des frameworks comme React, qui semblent plus simples à première vue. Mais est-ce vraiment le cas ?

Abonnez-vous à notre chaîne

Pour profiter des prochaines vidéos sur Angular, abonnez-vous à la nouvelle chaîne YouTube !

Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

Server-Side Rendering (SSR) avec Angular

Le Server-Side Rendering est une technique puissante qui permet de générer le contenu de votre application côté serveur avant de l'envoyer au navigateur. Mais qu'est-ce que cela signifie concrètement ?

Comprendre le SSR avec un exemple concret

Imaginez un restaurant avec deux façons de servir :

  1. Sans SSR (Client-Side Rendering) : Le client reçoit une boîte vide et tous les ingrédients séparément. Il doit assembler lui-même son plat.
  2. Avec SSR : Le client reçoit son plat déjà préparé et assemblé, prêt à être consommé.

Pourquoi utiliser le SSR ?

AVANTAGES

  • Meilleur référencement (SEO)
  • Temps de chargement initial plus rapide
  • Meilleure performance sur les appareils peu puissants
  • Prévisualisation plus efficace sur les réseaux sociaux

Installation et Configuration

Option 1 : Nouveau projet avec SSR

bash
ng new my-app --ssr

Option 2 : Ajouter le SSR à un projet existant

bash
ng add @angular/ssr

ATTENTION

Assurez-vous d'avoir Angular 17+ pour utiliser les dernières fonctionnalités SSR

Structure du projet SSR

ts
// Importation des dépendances nécessaires
import { APP_BASE_HREF } from '@angular/common';          // Pour gérer l'URL de base de l'application
import { CommonEngine } from '@angular/ssr';              // Moteur SSR d'Angular
import express from 'express';                            // Framework web pour Node.js
import { fileURLToPath } from 'node:url';                // Pour convertir les URL en chemins de fichiers
import { dirname, join, resolve } from 'node:path';       // Pour manipuler les chemins de fichiers
import bootstrap from './src/main.server';                // Point d'entrée de l'application Angular côté serveur

// Fonction principale qui configure et retourne l'application Express
export function app(): express.Express {
  const server = express();
  
  // Configuration des chemins des dossiers
  const serverDistFolder = dirname(fileURLToPath(import.meta.url));     // Dossier de distribution du serveur
  const browserDistFolder = resolve(serverDistFolder, '../browser');     // Dossier de distribution du navigateur
  const indexHtml = join(serverDistFolder, 'index.server.html');        // Fichier HTML principal

  // Création du moteur SSR Angular
  const commonEngine = new CommonEngine();

  // Configuration du moteur de vue Express
  server.set('view engine', 'html');
  server.set('views', browserDistFolder);

  // Gestion des fichiers statiques
  // Tous les fichiers dans le dossier browser seront servis statiquement
  server.get('**', express.static(browserDistFolder, {
    maxAge: '1y',                // Cache d'un an pour les fichiers statiques
    index: 'index.html',
  }));

  // Gestion des routes Angular
  server.get('**', (req, res, next) => {
    // Extraction des informations de la requête
    const { protocol, originalUrl, baseUrl, headers } = req;

    // Rendu de l'application avec le moteur SSR
    commonEngine
      .render({
        bootstrap,                // Point d'entrée de l'application
        documentFilePath: indexHtml,  // Chemin vers le fichier HTML principal
        url: `${protocol}://${headers.host}${originalUrl}`,  // URL complète de la requête
        publicPath: browserDistFolder,  // Chemin vers les assets publics
        providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],  // Configuration de l'URL de base
      })
      .then((html) => res.send(html))    // Envoi du HTML généré au client
      .catch((err) => next(err));        // Gestion des erreurs
  });

  return server;
}

// Fonction de démarrage du serveur
function run(): void {
  const port = process.env['PORT'] || 4000;    // Port d'écoute (4000 par défaut)

  // Démarrage du serveur Express
  const server = app();
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

// Lancement de l'application
run();
ts
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';

const serverConfig: ApplicationConfig = {
  providers: [
    provideServerRendering()
  ]
};

export const config = mergeApplicationConfig(appConfig, serverConfig);
ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { config } from './app/app.config.server';

const bootstrap = () => bootstrapApplication(AppComponent, config);

export default bootstrap;

Lisez les commentaires pour comprendre le code. Nous allons se concentrer sur le fichier server.ts, et principalement sur commonEngine:

Explication de CommonEngine

La Route Universelle

typescript
server.get('**', (req, res, next) => {

EXPLICATION

Le ** signifie que ce middleware intercepte TOUTES les requêtes qui n'ont pas été traitées par les middlewares précédents. C'est notre "filet de sécurité" qui assure que toutes les routes Angular seront gérées.

Extraction des Informations de Requête

typescript
const { protocol, originalUrl, baseUrl, headers } = req;

Décomposons chaque élément :

  • protocol : 'http' ou 'https'
  • originalUrl : le chemin complet demandé (ex: '/users/profile')
  • baseUrl : l'URL de base de l'application
  • headers : les en-têtes de la requête, dont headers.host (ex: 'localhost:4000')

Configuration du Rendu

typescript
commonEngine.render({
    bootstrap,      
    documentFilePath: indexHtml,  
    url: `${protocol}://${headers.host}${originalUrl}`,  
    publicPath: browserDistFolder,
    providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
})
OptionDescriptionExemple
bootstrapLe point d'entrée de votre application Angular défini dans main.server.ts. C'est la fonction qui initialise l'application côté serveur.bootstrap importé depuis main.server.ts
documentFilePathLe chemin vers le template HTML qui servira de base pour le rendu. C'est généralement le fichier index.html de votre application.src/index.html
urlL'URL complète reconstituée à partir du protocole, de l'hôte et du chemin. Elle permet au routeur Angular de déterminer quelle page générer.https://localhost:4000/users/profile
publicPathLe chemin vers le dossier contenant les fichiers statiques de l'application (CSS, images, JavaScript, etc.). C'est généralement le dossier de build.dist/browser
providersConfiguration supplémentaire pour l'injection de dépendances Angular. Permet notamment de définir l'URL de base avec APP_BASE_HREF et d'autres services nécessaires au SSR.[{ provide: APP_BASE_HREF, useValue: baseUrl }]

Gestion de la Réponse

typescript
.then((html) => res.send(html))    // Succès : envoi du HTML
.catch((err) => next(err));        // Erreur : passage au middleware d'erreur
  1. Le moteur SSR génère le HTML complet de la page
  2. En cas de succès : le HTML est envoyé au navigateur
  3. En cas d'erreur : l'erreur est transmise au gestionnaire d'erreurs d'Express

Chaque mois, recevez en avant-première notre newsletter avec les dernières actualités, tutoriels, astuces et ressources Angular directement par email !