Lab 25 : Rendu côté serveur (SSR)
📖 Ressources
Section intitulée « 📖 Ressources »🚀 Code de départ
Section intitulée « 🚀 Code de départ »album-wholesale-v17-signal-pre-jest
Dans ce lab, vous activerez le rendu côté serveur dans l’application album wholesale. Le SSR pré-rend les pages sur le serveur, améliorant le temps de chargement initial et le référencement. Vous gérerez les API réservées au navigateur en toute sécurité, transférerez l’état pour éviter les double-fetches et exécuterez le serveur SSR localement.
💡 Fonctionnement du SSR
Section intitulée « 💡 Fonctionnement du SSR »Avec le rendu côté client (CSR), le serveur envoie un squelette HTML vide — le navigateur reçoit <app-root></app-root> vide, puis télécharge et exécute le JavaScript pour construire la page.
Avec le rendu côté serveur (SSR), le serveur génère le HTML complet en premier. Le navigateur reçoit une page complète, affichée immédiatement et indexable par les moteurs de recherche. Angular hydrate ensuite la page — il attache les écouteurs d’événements et prend le relais sans re-rendu.
CSR : Serveur → HTML vide → navigateur télécharge JS → navigateur rendSSR : Serveur rend le HTML → navigateur affiche immédiatement → Angular hydrate📝 Instructions
Section intitulée « 📝 Instructions »Étape 1 : Ajouter le SSR au projet
Section intitulée « Étape 1 : Ajouter le SSR au projet »ng add @angular/ssrCe schematic fait tout le travail. Il :
- Installe
@angular/ssretexpress - Crée
src/main.server.ts— point d’entrée du bootstrap côté serveur - Crée
server.ts— le serveur HTTP Express à la racine du projet - Crée
src/app/app.config.server.ts— providers spécifiques au serveur - Met à jour
angular.jsonavec les options de build"server"et"ssr" - Ajoute un script
serve:ssrdanspackage.json
Étape 2 : Examiner la configuration serveur
Section intitulée « Étape 2 : Examiner la configuration serveur »Inspectez le fichier généré app.config.server.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);Étape 3 : Protéger le code réservé au navigateur
Section intitulée « Étape 3 : Protéger le code réservé au navigateur »Le serveur n’a pas accès à window, localStorage ni document. Protégez le code navigateur avec isPlatformBrowser :
import { PLATFORM_ID, inject } from '@angular/core';import { isPlatformBrowser } from '@angular/common';
export class AlbumDetailComponent { private platformId = inject(PLATFORM_ID);
ngOnInit() { if (isPlatformBrowser(this.platformId)) { // Accès sécurisé à window, localStorage, etc. const saved = localStorage.getItem('recentAlbums'); } }}Étape 4 : Utiliser afterNextRender pour l’accès au DOM
Section intitulée « Étape 4 : Utiliser afterNextRender pour l’accès au DOM »Préférez afterNextRender (Angular 16+) aux hooks de cycle de vie pour les effets secondaires navigateur :
import { afterNextRender } from '@angular/core';
export class AlbumPlayerComponent { constructor() { afterNextRender(() => { // S'exécute uniquement dans le navigateur, après le premier rendu this.initAudioPlayer(); }); }}Étape 5 : Transférer l’état pour éviter le double fetch
Section intitulée « Étape 5 : Transférer l’état pour éviter le double fetch »Sans transfer state, Angular re-récupère les données dans le navigateur même si le serveur les a déjà récupérées. Corrigez cela avec TransferState :
import { TransferState, makeStateKey } from '@angular/core';import { inject } from '@angular/core';
const ALBUMS_KEY = makeStateKey<Album[]>('albums');
export class AlbumService { private transferState = inject(TransferState); private http = inject(HttpClient);
getAlbums(): Observable<Album[]> { const cached = this.transferState.get(ALBUMS_KEY, null); if (cached) { this.transferState.remove(ALBUMS_KEY); return of(cached); } return this.http.get<Album[]>('/api/albums').pipe( tap(albums => this.transferState.set(ALBUMS_KEY, albums)) ); }}Activez également l’hydratation dans app.config.ts :
import { provideClientHydration } from '@angular/platform-browser';
export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes), provideHttpClient(), provideClientHydration(), ],};Étape 6 : Construire et démarrer le serveur SSR
Section intitulée « Étape 6 : Construire et démarrer le serveur SSR »ng buildCela génère deux dossiers de sortie dans dist/<nom-du-projet>/ :
browser/— ressources statiques servies au clientserver/— bundle Node.js
Astuce : Le chemin exact correspond à
outputPathdansangular.json. Vérifiez avecls dist/.
Démarrez le serveur :
node dist/album-wholesale-v17/server/server.mjsOuvrez http://localhost:4000 et vérifiez :
- Source de la page (Ctrl+U) — le HTML doit déjà contenir le contenu des albums
- Onglet Réseau — aucun appel API supplémentaire après l’hydratation
Étape 7 : Vérifier l’amélioration SEO
Section intitulée « Étape 7 : Vérifier l’amélioration SEO »Ouvrez la source de la page et confirmez :
- La balise
<title>contient le nom de l’album - La méta description est présente
- Le HTML de la liste d’albums est visible dans la source (pas seulement
<app-root></app-root>)
Cela confirme que la page est rendue côté serveur avant d’être envoyée au navigateur.
✅ Validation en classe
Section intitulée « ✅ Validation en classe »La démonstration la plus claire que le SSR fonctionne : ouvrir DevTools → Réseau → rafraîchir la page et cliquer sur la requête du document HTML initial. Regarder l’onglet Réponse.
- CSR : on voit
<app-root></app-root>— vide, aucun contenu - SSR : on voit le HTML complet de la liste d’albums dans
<app-root>— rendu côté serveur
Aucun plugin nécessaire. Cette comparaison simple rend la valeur du SSR immédiatement tangible.

