Angular CLI : HMR, esbuild et Angular DevTools

🏷️ Front-end 📅 17/04/2026 14:00:00 👤 Mezgani said
Angular Angular 19 Angular 20 Angular Cli Hmr Esbuild Vite Angular Devtools Performance Angular
Angular CLI : HMR, esbuild et Angular DevTools

Maîtrisez le tooling Angular 19/20 : HMR des styles et templates, builder esbuild/Vite, migration depuis Webpack, Angular DevTools v2 et ng generate optimisé.

HMR des styles en Angular 19

Depuis Angular 19, le Hot Module Replacement (HMR) des styles est activé par défaut lors du développement. Concrètement, modifier un fichier CSS, SCSS ou LESS déclenche une mise à jour immédiate dans le navigateur, sans rechargement complet de la page et sans perdre l'état applicatif courant.

Avant cette fonctionnalité, chaque modification de style forçait un rechargement complet : l'état des formulaires, des onglets ouverts et des données locales était perdu. Le gain de productivité est immédiat dès les premiers jours d'utilisation.

Comportement par défaut

Avec Angular CLI 19+, ng serve active automatiquement le HMR des styles. Aucune configuration n'est requise pour en bénéficier. La valeur de hmr dans les options de serve est désormais true par défaut.

// angular.json — configuration de serve pour le HMR des styles
// Angular 19 active le HMR par défaut, mais voici comment le contrôler explicitement

{
  "projects": {
    "mon-app": {
      "architect": {
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            // HMR des styles activé par défaut depuis Angular 19
            // Mettre à false uniquement si vous rencontrez un problème
            "hmr": true
          },
          "configurations": {
            "production": {
              // En production, HMR est toujours désactivé automatiquement
              "hmr": false
            }
          }
        }
      }
    }
  }
}
A retenir : le HMR des styles est actif par défaut avec ng serve sous Angular 19+. Vous n'avez rien à configurer pour en profiter immédiatement.

Gain de productivité mesuré

Sur un projet type, l'itération CSS passe de 2 à 4 secondes (rechargement complet) à moins de 100 ms (injection à chaud). Sur une journée de travail frontend intensif, cela représente plusieurs minutes économisées par heure de développement.

Scénario Sans HMR Avec HMR Angular 19
Modification d'un fichier SCSS ~2-4 s (rechargement page) < 100 ms (injection directe)
État applicatif conservé Non (reset complet) Oui
Position scroll conservée Non Oui
Données de formulaires conservées Non Oui

HMR des templates sans perte d'état

Angular 19 introduit également le HMR des templates. Lorsque vous modifiez le fichier HTML d'un composant, Angular ne recharge plus l'application entière : il met à jour uniquement les instances du composant concerné dans le DOM, en préservant l'état des Signals, services et données locales.

Cette fonctionnalité est particulièrement utile lors du développement de composants complexes dont l'état met du temps à se reconstituer (chargement de données, navigation entre étapes, état d'un formulaire multi-pages).

Note : le HMR des templates est activé par défaut depuis Angular 19.1. Pour les versions 19.0.x, vérifiez que votre CLI est à jour avec ng update @angular/cli @angular/core.

Comment Angular applique le HMR de template

Lorsqu'un fichier template est sauvegardé, le serveur de développement envoie uniquement le nouveau template compilé au navigateur via WebSocket. Angular identifie toutes les instances actives du composant et les re-rend avec le nouveau template, sans recréer le contexte (services injectés, état des Signals, inputs reçus).

// Exemple : composant dont l'état est préservé lors du HMR de template
import { Component, signal, computed } from '@angular/core';

@Component({
  selector: 'app-panier',
  standalone: true,
  // Modifiez ce template : les articles chargés ne disparaissent pas
  template: `
    <h2>Mon panier ({{ totalArticles() }} articles)</h2>
    <ul>
      @for (article of articles(); track article.id) {
        <li>{{ article.nom }} — {{ article.prix }} €</li>
      }
    </ul>
    <p class="total">Total : {{ total() }} €</p>
  `
})
export class PanierComponent {
  // Ces Signals conservent leur valeur lors d'un HMR de template
  articles = signal([
    { id: 1, nom: 'Clavier mécanique', prix: 89 },
    { id: 2, nom: 'Souris ergonomique', prix: 45 }
  ]);

  // Computed recalculé automatiquement — non perdu lors du HMR
  totalArticles = computed(() => this.articles().length);
  total         = computed(() => this.articles().reduce((s, a) => s + a.prix, 0));
}
A retenir : le HMR de template préserve les Signals, les inputs et les données injectées. Seul le rendu visuel est mis à jour. C'est le cas d'usage idéal pour affiner l'UI d'un composant chargé de données.

Limites actuelles du HMR de template

  • Les modifications du fichier TypeScript du composant (logique, cycle de vie) déclenchent encore un rechargement complet.
  • Les changements dans les providers ou la configuration d'injection nécessitent également un rechargement.
  • Le HMR est uniquement actif en mode ng serve — jamais en production.

Builder esbuild et Vite : gains de performance

Depuis Angular 17, le builder basé sur esbuild et Vite est l'option recommandée. Il remplace progressivement Webpack comme moteur de compilation. En Angular 19 et 20, ce builder est stable, activé par défaut pour les nouveaux projets, et apporte des gains de performance significatifs à tous les stades du développement.

Pourquoi esbuild est plus rapide que Webpack

esbuild est écrit en Go et exploite pleinement le parallélisme multi-cœur. Contrairement à Webpack (JavaScript, mono-thread pour les transformations), esbuild peut traiter plusieurs fichiers simultanément. Le résultat est une vitesse de compilation entre 10x et 100x plus rapide selon la taille du projet.

Vite joue le rôle de serveur de développement : il exploite les ES modules natifs du navigateur pour servir les fichiers à la demande, sans regroupement préalable. Les rebuilds partiels sont quasi-instantanés.

Métrique Webpack (ancien) esbuild/Vite (Angular 19+)
Cold start (ng serve) ~15-40 s ~3-8 s
Rebuild incrémental (fichier TS) ~4-10 s < 500 ms
Build production (ng build) ~60-180 s ~15-40 s
Consommation mémoire Élevée (Node.js Webpack) Réduite (Go natif)
Tree-shaking Oui (via Terser) Oui (natif, plus agressif)
Note : les chiffres varient selon la taille du projet, le nombre de dépendances et la machine. Mesurez toujours sur votre propre base de code avec ng build --stats-json pour comparer objectivement.

Migration webpack vers esbuild : étapes et angular.json

Si votre projet Angular est en version 16 ou antérieure, ou si vous avez créé le projet avant Angular 17, il utilise probablement encore le builder Webpack. Voici comment migrer proprement vers esbuild en préservant votre configuration existante.

Étape 1 : mettre à jour Angular CLI et Core

# Mise à jour vers la dernière version stable d'Angular 19
# Toujours mettre à jour CLI et Core ensemble
ng update @angular/cli @angular/core

# Vérifier la version installée après mise à jour
ng version

Étape 2 : modifier le builder dans angular.json

La migration se fait en changeant deux valeurs dans angular.json : le builder de build et celui de serve.

// angular.json — migration de Webpack vers esbuild
// Avant (Webpack) :
// "builder": "@angular-devkit/build-angular:browser"
// "builder": "@angular-devkit/build-angular:dev-server"

// Après (esbuild + Vite) :
{
  "projects": {
    "mon-app": {
      "architect": {
        "build": {
          // Nouveau builder esbuild — remplace "browser"
          "builder": "@angular-devkit/build-angular:application",
          "options": {
            "outputPath": "dist/mon-app",
            "index": "src/index.html",
            "browser": "src/main.ts",   // Renommé de "main" à "browser"
            "polyfills": ["zone.js"],
            "tsConfig": "tsconfig.app.json",
            "assets": ["src/favicon.ico", "src/assets"],
            "styles": ["src/styles.scss"],
            "scripts": []
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "500kB",
                  "maximumError": "1MB"
                }
              ],
              "outputHashing": "all"
            },
            "development": {
              // Optimisations désactivées en dev pour un rebuild plus rapide
              "optimization": false,
              "extractLicenses": false,
              "sourceMap": true
            }
          }
        },
        "serve": {
          // Nouveau serveur Vite — remplace "dev-server"
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": { "buildTarget": "mon-app:build:production" },
            "development": { "buildTarget": "mon-app:build:development" }
          },
          "defaultConfiguration": "development"
        }
      }
    }
  }
}
A retenir : la clé de la migration est de remplacer "builder": "…:browser" par "builder": "…:application" et de renommer main en browser dans les options. Le reste de la configuration est compatible.

Étape 3 : vérifier les incompatibilités

Certaines configurations Webpack personnalisées (via custom-webpack) ne sont pas directement portables vers esbuild. Points à vérifier :

  • Les plugins Webpack personnalisés doivent être recréés sous forme de plugins esbuild ou supprimés si devenus inutiles.
  • Les loaders CSS exotiques (ex: less-loader custom) peuvent nécessiter une configuration spécifique.
  • Les require() dynamiques non standard peuvent se comporter différemment — tester les lazy-loaded modules.
  • Les polyfills Node.js (Buffer, process) ne sont plus injectés automatiquement — à déclarer explicitement si nécessaires.

Étape 4 : valider le build de production

# Build production avec le nouveau builder esbuild
ng build --configuration production

# Analyser la taille des bundles générés
ng build --configuration production --stats-json
# Puis utiliser webpack-bundle-analyzer ou source-map-explorer :
npx source-map-explorer dist/mon-app/browser/*.js

Angular DevTools v2 : profiler Signals et flamegraph

Angular DevTools est l'extension Chrome et Firefox officielle pour déboguer et profiler les applications Angular. La version 2, disponible depuis Angular 17 et enrichie dans les versions 19 et 20, apporte deux fonctionnalités majeures : le profiler Signals et le flamegraph des composants.

Installation et accès

  • Installer l'extension Angular DevTools depuis le Chrome Web Store ou Firefox Add-ons.
  • Ouvrir Chrome DevTools (F12) → onglet Angular.
  • L'extension est active uniquement sur les applications Angular en mode développement (le mode production désactive l'accès à l'API d'inspection).

Explorateur de composants

L'onglet Components affiche l'arbre complet des composants de la page courante. Pour chaque composant, vous pouvez inspecter :

  • Les propriétés (inputs, outputs, state)
  • Les Signals et leur valeur courante
  • Les injections de dépendances
  • La hiérarchie parent/enfant
// Composant compatible Angular DevTools — les Signals sont visibles dans l'onglet Components
import { Component, signal, computed, inject } from '@angular/core';
import { UtilisateurService } from './utilisateur.service';

@Component({
  selector: 'app-profil',
  standalone: true,
  template: `
    <h1>Profil de {{ nomComplet() }}</h1>
    <p>Score : {{ score() }}</p>
  `
})
export class ProfilComponent {
  private utilisateurService = inject(UtilisateurService);

  // Ces Signals apparaissent dans Angular DevTools avec leur valeur en temps réel
  prenom = signal('Alice');
  nom    = signal('Dupont');
  score  = signal(1250);

  // Le computed est aussi visible et se met à jour en live dans DevTools
  nomComplet = computed(() => `${this.prenom()} ${this.nom()}`);
}

Profiler : le flamegraph des composants

L'onglet Profiler d'Angular DevTools permet d'enregistrer un cycle de détection de changements et de visualiser sous forme de flamegraph le temps passé dans chaque composant. Cela permet d'identifier précisément les composants qui ralentissent la détection.

Avec Angular 19 et les Signals, le profiler est enrichi : il distingue les re-rendus déclenchés par un Signal de ceux causés par la détection de changements classique (zone.js). Cette granularité permet de valider l'efficacité de votre migration vers une architecture Signal-based.

A retenir : utilisez le Profiler d'Angular DevTools pour identifier les composants qui se re-rendent trop souvent. Un composant avec ChangeDetectionStrategy.OnPush ou basé sur des Signals ne devrait apparaître dans le flamegraph que lorsqu'une de ses dépendances change réellement.

Bonnes pratiques avec Angular DevTools

  • Toujours profiler sur Chrome avec le throttling CPU activé (4x slowdown) pour simuler des machines moins puissantes.
  • Filtrer le flamegraph par durée pour ne cibler que les composants au-dessus de 5 ms.
  • Comparer un enregistrement avant et après avoir appliqué OnPush sur un composant lourd.
  • Utiliser l'inspecteur de Signals pour vérifier qu'aucun Signal ne se met à jour inutilement lors d'une interaction.

ng generate amélioré : standalone par défaut

Depuis Angular 17, et de façon définitive dans Angular 19, ng generate component crée des composants standalone par défaut. La configuration d'Angular CLI a évolué pour refléter ce changement et réduire le nombre de fichiers générés.

Comportement par défaut dans Angular 19

# Génère un composant standalone par défaut (Angular 19+)
# Aucun NgModule n'est créé ou modifié automatiquement
ng generate component features/tableau-de-bord

# Équivalent explicite (redondant en Angular 19, mais clarifiant)
ng generate component features/tableau-de-bord --standalone

# Génère sans fichier de test (accélère la génération initiale)
ng generate component features/tableau-de-bord --skip-tests

# Génère avec template et styles inline (moins de fichiers)
ng generate component features/tableau-de-bord --inline-template --inline-style

Le composant généré est le suivant :

// tableau-de-bord.component.ts — généré par ng generate (Angular 19)
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-tableau-de-bord',
  // standalone: true est la valeur par défaut depuis Angular 19
  standalone: true,
  // CommonModule importé automatiquement pour ngIf, ngFor, etc.
  imports: [CommonModule],
  templateUrl: './tableau-de-bord.component.html',
  styleUrl: './tableau-de-bord.component.scss'  // styleUrl (singulier) depuis Angular 17
})
export class TableauDeBordComponent {
  // Corps du composant — à compléter selon vos besoins
}
Note : remarquez styleUrl (singulier) au lieu de l'ancien styleUrls (pluriel avec tableau). Cette simplification est introduite dans Angular 17 et maintenue dans les versions suivantes.

Schematics CLI disponibles en Angular 19

Commande Alias Résultat
ng g component ng g c Composant standalone (fichiers .ts, .html, .scss)
ng g service ng g s Service injectable avec providedIn: 'root'
ng g directive ng g d Directive standalone
ng g pipe ng g p Pipe standalone et pur par défaut
ng g guard ng g gu Guard fonctionnel (CanActivateFn)
ng g interceptor ng g i Interceptor fonctionnel (HttpInterceptorFn)
ng g resolver ng g r Resolver fonctionnel (ResolveFn)

Configurer les schematics dans angular.json

Il est possible de définir des valeurs par défaut pour tous les schematics afin d'éviter de répéter les mêmes options à chaque commande :

// angular.json — configuration globale des schematics
{
  "projects": {
    "mon-app": {
      "schematics": {
        // Paramètres par défaut pour tous les composants générés
        "@schematics/angular:component": {
          "standalone": true,      // Standalone par défaut (déjà vrai en Angular 19)
          "style": "scss",         // SCSS comme style par défaut
          "skipTests": false,      // Garder les fichiers de tests (bonne pratique)
          "changeDetection": "OnPush"  // OnPush par défaut pour la performance
        },
        // Paramètres pour les directives
        "@schematics/angular:directive": {
          "standalone": true
        },
        // Paramètres pour les pipes
        "@schematics/angular:pipe": {
          "standalone": true
        }
      }
    }
  }
}
A retenir : configurer "changeDetection": "OnPush" par défaut dans les schematics est une excellente pratique. Cela force chaque nouveau composant à optimiser sa détection de changements dès la création, sans effort supplémentaire.

Configuration complète angular.json pour Angular 19

Voici une configuration angular.json complète et commentée, intégrant esbuild, HMR, schematics optimisés et budgets de performance. C'est un point de départ solide pour tout nouveau projet Angular 19.

// angular.json — configuration complète Angular 19 avec esbuild + HMR
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "mon-app": {
      "projectType": "application",
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",

      // Schematics : valeurs par défaut pour ng generate
      "schematics": {
        "@schematics/angular:component": {
          "standalone": true,
          "style": "scss",
          "changeDetection": "OnPush"
        },
        "@schematics/angular:directive": { "standalone": true },
        "@schematics/angular:pipe": { "standalone": true }
      },

      "architect": {
        // Build principal avec esbuild
        "build": {
          "builder": "@angular-devkit/build-angular:application",
          "options": {
            "outputPath": "dist/mon-app",
            "index": "src/index.html",
            "browser": "src/main.ts",
            "polyfills": ["zone.js"],
            "tsConfig": "tsconfig.app.json",
            "inlineStyleLanguage": "scss",
            "assets": [
              { "glob": "**/*", "input": "public" }
            ],
            "styles": ["src/styles.scss"],
            "scripts": []
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "500kB",
                  "maximumError": "1MB"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "4kB",
                  "maximumError": "8kB"
                }
              ],
              "outputHashing": "all"
            },
            "development": {
              "optimization": false,
              "extractLicenses": false,
              "sourceMap": true
            }
          },
          "defaultConfiguration": "production"
        },

        // Serveur de développement Vite avec HMR
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            // HMR des styles et templates activé par défaut
            "hmr": true
          },
          "configurations": {
            "production": { "buildTarget": "mon-app:build:production" },
            "development": { "buildTarget": "mon-app:build:development" }
          },
          "defaultConfiguration": "development"
        },

        // Extraction des traductions i18n
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n"
        },

        // Tests unitaires (Jest ou Karma)
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "polyfills": ["zone.js", "zone.js/testing"],
            "tsConfig": "tsconfig.spec.json",
            "inlineStyleLanguage": "scss",
            "assets": [
              { "glob": "**/*", "input": "public" }
            ],
            "styles": ["src/styles.scss"],
            "scripts": []
          }
        }
      }
    }
  }
}
Note : dans Angular 19, le dossier public/ remplace l'ancien dossier src/assets/ pour les ressources statiques. Les fichiers placés dans public/ sont copiés à la racine du build sans sous-dossier assets/.

Vérifier la configuration active

# Afficher la version de CLI et les builders disponibles
ng version

# Afficher les schematics disponibles dans le projet
ng generate --list-schematics

# Lancer le serveur de développement avec esbuild + HMR
ng serve --configuration development

# Build de production optimisé avec esbuild
ng build --configuration production --progress

Conclusion

Angular 19 et 20 marquent une étape décisive dans l'évolution du tooling Angular. Le HMR des styles et des templates transforme concrètement l'expérience de développement en éliminant les rechargements coûteux, tandis que le builder esbuild/Vite divise les temps de compilation par 3 à 10 selon la taille du projet. Ces améliorations ne nécessitent pas de réécriture de code métier : elles s'activent par simple mise à jour de la CLI et adaptation de angular.json.

Combinez ces gains de performance avec Angular DevTools v2 pour profiler vos Signals et repérer les composants à optimiser, et configurez vos schematics une seule fois pour que chaque nouveau composant bénéficie automatiquement d'OnPush et du standalone. Votre cycle de développement sera plus rapide, plus agréable et votre application plus performante en production.

A retenir : pour bénéficier de tout le tooling moderne Angular 19, trois actions suffisent — mettre à jour CLI et Core (ng update), migrer le builder vers application dans angular.json, et configurer les schematics avec OnPush et standalone par défaut.