Retour aux articles

Intégration et optimisation du Server-Side Rendering (SSR) avec Angular Universal

Intégration et optimisation du Server-Side Rendering (SSR) avec Angular Universal | Laty Gueye Samba - Développeur Full Stack Dakar Sénégal, Expert Java Spring Boot Angular

Intégration et optimisation du Server-Side Rendering (SSR) avec Angular Universal

Dans l'écosystème du développement web moderne, la performance et l'optimisation pour les moteurs de recherche (SEO) sont des piliers fondamentaux. Les applications monopages (SPA) construites avec des frameworks comme Angular excellent dans l'expérience utilisateur interactive, mais elles peuvent parfois rencontrer des défis en matière de référencement naturel et de temps de chargement initial. Le contenu des SPA étant principalement rendu côté client, les robots d'indexation et les utilisateurs avec des connexions lentes peuvent être confrontés à un écran vide ou à un chargement prolongé.

Pour contrer ces inconvénients, le Server-Side Rendering (SSR) s'est imposé comme une solution robuste. Il permet de générer la première version d'une page HTML sur le serveur avant de l'envoyer au navigateur, assurant ainsi une meilleure visibilité pour les moteurs de recherche et un affichage plus rapide du contenu initial. Angular Universal est l'outil officiel fourni par l'équipe Angular pour implémenter cette capacité SSR, transformant des applications Angular classiques en des expériences web hautement optimisées.

Cet article explore l'intégration et les stratégies d'optimisation du SSR avec Angular Universal, une compétence essentielle pour tout développeur Full Stack visant à construire des applications performantes et bien référencées, à l'image des solutions développées par Laty Gueye Samba, Développeur Full Stack basé à Dakar, Sénégal, expert en Java Spring Boot et Angular.

Comprendre Angular Universal et ses avantages fondamentaux

Angular Universal permet aux applications Angular de s'exécuter sur le serveur, générant une version statique de l'application. Cette version HTML pré-rendue est ensuite envoyée au client. Une fois le JavaScript de l'application chargé côté navigateur, Angular "hydrate" l'application, reprenant le contrôle de l'interface utilisateur et permettant une navigation et une interactivité fluides. Ce processus, souvent appelé "hydratation", est crucial pour maintenir les performances.

Les avantages d'Angular SSR via Universal sont multiples et significatifs :

  • Amélioration du SEO : Les moteurs de recherche peuvent explorer et indexer plus facilement le contenu de l'application puisque le HTML est disponible dès la première requête. Cela est particulièrement important pour les sites riches en contenu.
  • Performances accrues : Le First Contentful Paint (FCP) est significativement réduit. L'utilisateur voit le contenu de la page beaucoup plus rapidement, ce qui améliore la perception de la vitesse et réduit les taux de rebond.
  • Meilleure expérience utilisateur : Sur des appareils moins puissants ou des connexions réseau lentes, le chargement initial est plus rapide et moins gourmand en ressources côté client, offrant une expérience utilisateur plus fluide dès le départ.
  • Accessibilité : Le contenu statique initial est plus accessible pour les utilisateurs ayant des navigateurs désactivant JavaScript ou des lecteurs d'écran.

Mise en œuvre initiale de SSR avec Angular Universal

L'intégration d'Angular Universal dans un projet Angular existant est un processus bien structuré. Le CLI Angular simplifie grandement cette tâche. Voici les étapes clés pour configurer le SSR :

1. Ajout d'Angular Universal au projet

La commande suivante ajoute les dépendances nécessaires et configure le projet pour le SSR :

ng add @nguniversal/express-engine

Cette commande génère plusieurs fichiers importants, notamment :

  • server.ts : Le point d'entrée pour le serveur Node.js qui servira l'application. Il utilise Express.js pour gérer les requêtes.
  • main.server.ts : Le point d'entrée de l'application Angular côté serveur, qui exporte un module qui peut être bootstrappé.
  • tsconfig.server.json : Un fichier de configuration TypeScript spécifique pour la compilation côté serveur.

2. Compilation de l'application pour le SSR

Après l'ajout d'Universal, des scripts sont automatiquement ajoutés au fichier package.json pour construire l'application pour le SSR :

npm run build:ssr

Cette commande compile l'application deux fois : une fois pour le navigateur (client) et une fois pour le serveur (Universal). Le résultat est un bundle client dans dist/browser et un bundle serveur dans dist/server.

3. Démarrage du serveur SSR

Pour tester l'application en mode SSR, une autre commande est fournie :

npm run serve:ssr

Ceci lance le serveur Express.js défini dans server.ts, qui est responsable de la pré-génération des pages HTML et de leur envoi au navigateur. Lorsque l'application est consultée, la première page est servie par le serveur, puis l'application Angular prend le relais côté client.

Un extrait simplifié du fichier server.ts pourrait ressembler à ceci, illustrant la base de l'initialisation du moteur de rendu Angular Universal avec Express :

import 'zone.js/node';

import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';

import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';

export function app(): express.Express {
  const server = express();
  const distFolder = join(process.cwd(), 'dist/browser'); // ou 'dist/your-app-name/browser' selon la version d'Angular

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Notre moteur de rendu Angular Universal
  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
  }));

  // Servir les fichiers statiques
  server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }));

  // Toutes les routes doivent être gérées par Angular Universal
  server.get('*', (req, res) => {
    res.render('index', { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
  });

  return server;
}

function run(): void {
  const port = process.env['PORT'] || 4000;

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

// Démarrer le serveur
run();

Stratégies d'optimisation avancées pour Angular SSR

L'intégration initiale est une première étape, mais l'optimisation est essentielle pour exploiter pleinement le potentiel d'Angular Universal. Laty Gueye Samba, en tant que Développeur Full Stack, met l'accent sur l'efficacité et la robustesse dans les applications qu'il développe.

1. Gestion du TransferState

Lorsque le serveur effectue des requêtes HTTP pour récupérer des données et les afficher, ces mêmes requêtes seraient normalement refaites côté client. Le service TransferState permet de transférer l'état des données du serveur au client, évitant ainsi des requêtes redondantes et améliorant les performances.

import { makeStateKey, TransferState, Injectable } from '@angular/platform-browser';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';

const MY_DATA_KEY = makeStateKey<any>('myData');

@Injectable({ providedIn: 'root' })
export class DataService {
  constructor(private http: HttpClient, private transferState: TransferState) {}

  getData(): Observable<any> {
    const data = this.transferState.get(MY_DATA_KEY, null);
    if (data) {
      return of(data); // Données déjà présentes du SSR
    } else {
      return this.http.get('/api/my-data').pipe(
        tap(response => {
          this.transferState.set(MY_DATA_KEY, response); // Stocker les données côté serveur
        })
      );
    }
  }
}

2. Éviter les APIs spécifiques au navigateur

Le code exécuté côté serveur ne dispose pas d'accès aux objets du DOM (window, document) ni à d'autres APIs spécifiques au navigateur. Il est crucial de s'assurer que le code ne tente pas d'accéder à ces APIs pendant le rendu côté serveur. Utilisez les injections PLATFORM_ID et la fonction isPlatformBrowser ou isPlatformServer pour conditionner l'exécution de code :

import { Component, OnInit, PLATFORM_ID, Inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

@Component({ /* ... */ })
export class MyComponent implements OnInit {
  constructor(@Inject(PLATFORM_ID) private platformId: Object) {}

  ngOnInit() {
    if (isPlatformBrowser(this.platformId)) {
      // Ce code ne s'exécute que dans le navigateur
      console.log('Exécution côté navigateur');
      // window.localStorage.getItem(...) ou d'autres APIs du navigateur
    } else {
      // Ce code s'exécute côté serveur
      console.log('Exécution côté serveur');
    }
  }
}

3. Optimisation du bundle et Lazy Loading

Le Lazy Loading des modules fonctionne également avec Angular Universal. S'assurer que les modules lourds sont chargés paresseusement peut réduire la taille du bundle initial, même si le rendu initial est effectué par le serveur. Le serveur ne chargera que les modules nécessaires pour la route demandée, ce qui contribue à une meilleure performance globale.

4. Caching côté serveur

Pour les pages dont le contenu ne change pas fréquemment, il est possible d'implémenter un système de cache au niveau du serveur Express.js. Cela permet de servir des pages pré-rendues sans avoir à re-générer la vue à chaque requête, réduisant ainsi la charge du serveur et améliorant les temps de réponse.

5. Utilisation de l'API Prerender CLI

Pour les applications dont le contenu est majoritairement statique ou peu changeant, l'API Prerender de Angular CLI (npm run prerender) peut générer des fichiers HTML statiques pour toutes les routes au moment du build. Ces fichiers peuvent ensuite être servis directement par un CDN ou un serveur web, offrant des performances optimales sans la surcharge d'un serveur Node.js en production.

Point de vue : développeur full stack à Dakar

Pour un développeur travaillant sur des systèmes comme des applications de gestion des risques ou des plateformes de gestion hospitalière, la maîtrise de l'intégration et de l'optimisation du Server-Side Rendering (SSR) avec Angular Universal représente un avantage concurrentiel réel sur le marché technologique africain, en pleine expansion. Cette expertise permet de délivrer des applications à la fois performantes pour l'utilisateur final et optimisées pour le référencement, répondant ainsi aux exigences des projets modernes.

Conclusion

L'intégration et l'optimisation du Server-Side Rendering avec Angular Universal sont devenues des pratiques indispensables pour les applications Angular modernes. Elles permettent de surmonter les limitations inhérentes au client-side rendering en offrant une meilleure performance, une expérience utilisateur améliorée et un référencement naturel optimisé. Pour un Développeur Full Stack expert en Java Spring Boot et Angular, comme Laty Gueye Samba, la maîtrise de ces techniques est un atout majeur pour la conception et la réalisation de solutions robustes et efficaces.

En adoptant ces stratégies d'optimisation, les développeurs peuvent s'assurer que leurs applications Angular sont non seulement interactives et réactives, mais aussi performantes dès le premier chargement et visibles sur le web, un équilibre essentiel dans le paysage numérique actuel.

Ressources officielles :

À propos de l'auteur

Laty Gueye Samba est développeur Full Stack basé à Dakar, Sénégal. Spécialiste des écosystèmes Java / Spring Boot et Angular.

Contact : latygueyesamba@gmail.com  |  Dakar, Sénégal