← Retour au blog
AngularMigrationZone.jsSSRPerformance

Migration Angular 18 → 19 : les ruptures qui comptent vraiment

Angular 19 n'est pas une simple mise à jour cosmétique. Sortie en novembre 2024, elle consolide la direction prise par Angular 18 autour des signaux et du rendu côté serveur, mais impose aussi des ruptures sérieuses. La plus visible : Zone.js devient optionnel mais fortement découragé pour les nouvelles apps, au profit de la détection de changements basée sur les signaux. Parallèlement, l'hydratation SSR passe à un nouveau mécanisme plus strict, et les validateurs réactifs héritent d'une API partiellement remaniée. Ces changements ne cassent pas tout, mais ils demandent de la rigueur dans le diagnostic et l'ordre des étapes.

Zone.js : fin de l'ère des event listeners passifs

Depuis Angular 2, Zone.js était le cœur du système de détection de changements. Angular 18 l'a rendu optionnel en le signalant dans angular.json. Angular 19 va plus loin : les nouvelles applications générées par ng new n'incluent plus Zone.js du tout, et les équipes existantes sont fortement incitées à la retirer. Le changement est radical mais libérateur. Sans Zone.js, votre bundle diminue d'environ 30 KB (non minifié), et plus important encore, vous gagnez un contrôle explicite sur la détection de changements via les signaux et le système d'observables. Cependant, cette migration impose de revoir tous les endroits où vous appeliez zone.run(), zone.runOutsideAngular(), ou comptiez sur le comportement implicite de Zone.js pour déclencher la détection. Si vous utilisez des setTimeout, des promesses non résolues ou des événements DOM bruts, vous devez les envelopper manuellement avec ChangeDetectorRef.markForCheck() ou passer à des observables avec async pipe.

La migration sans Zone.js est simple en théorie : supprimez l'import de Zone.js dans polyfills.ts, puis retirez-le du angular.json. En pratique, testez d'abord en parallèle en gardant Zone.js et en observant les logs Angular DevTools pour identifier les zones mortes (code qui ne déclenche pas la détection). Cherchez les patterns comme zone.run(callback) ou ngZone.runOutsideAngular(). Remplacez-les par des appels directs à ChangeDetectorRef ou par des signaux wrappés dans effect(). Un piège courant : les timers et les animations. Si vous utilisiez setInterval sans Zone.js, Angular ne saura pas qu'il faut mettre à jour la vue. Solution : enveloppez avec effect() ou utilisez un observable avec interval() + takeUntilDestroyed().

Hydratation SSR : stricte et sans filet

Angular 18 introduisait l'hydratation pour passer le DOM côté serveur au client sans re-render. Angular 19 la rend plus stricte. Le nouveau système, appelé "non-destructive hydration", exige que le DOM généré côté serveur corresponde exactement au DOM attendu côté client. Si le client génère une structure différente, l'hydratation échoue silencieusement (ou avec un warning en développement), et Angular re-render tout le composant. C'est plus performant à long terme, mais pendant la migration, cela révèle des bugs subtils : des async pipe qui résolvent différemment, des *ngIf conditionnés sur des valeurs de timing, des zones de texte qui changent entre SSR et CSR.

Pour migrer, activez d'abord la nouvelle hydratation en mettant à jour app.config.ts avec provideClientHydration() (la signature reste la même, mais le comportement interne change). Puis lancez ng build --ssr et vérifiez les logs côté client. Angular 19 affiche un avertissement pour chaque mismatch détecté. Corrigez-les un par un : généralement en rendant les données initiales stables (utilisez une seed pour les générateurs aléatoires en SSR, freeze les timestamps au moment du rendu serveur, etc.). Un exemple concret : si vous affichez new Date().toLocaleString(), le serveur et le client auront des résultats différents. Solution : passez la date en tant que propriété d'entrée depuis le serveur, ou utilisez une pipe personnalisée qui accepte une valeur fixée. Testez en dev avec ng serve --ssr pour voir les mismatches en temps réel.

Validateurs et FormControl : API unifiée

Angular 18 avait commencé à refondre les validateurs avec une nouvelle API basée sur les signaux. Angular 19 achève le travail en déprépréciant l'ancienne syntaxe basée sur les chaînes de caractères et les classes. Les validateurs synchrones et asynchrones ont maintenant une signature unifiée : (control: AbstractControl) => ValidationErrors | null. Cette unification simplifie le mental model, mais elle brise le code qui utilisait la syntaxe de décorateurs ou les validateurs imbriqués dans des FormGroup avec des options legacy.

Commencez par auditer votre code : cherchez les validateurs déclarés comme Validators.minLength(5) ou Validators.required appliqués directement. Ils fonctionnent toujours, mais Angular les signale comme dépréciés. Migrez-les progressivement en utilisant la nouvelle API : à la place de new FormControl('', Validators.required), écrivez new FormControl('', { validators: [Validators.required], nonNullable: true }). Pour les validateurs personnalisés, le changement est minimal : assurez-vous que votre fonction retourne ValidationErrors | null, pas un objet arbitraire. Si vous aviez des validateurs asynchrones qui retournaient un Observable<ValidationErrors | null>, ils restent compatibles, mais Angular 19 préfère les validateurs qui retournent directement ValidationErrors | null avec une dépendance explicite sur les services via inject(). Testez chaque validateur en isolation avec ng test avant de les appliquer en masse.

Pièges courants et anti-patterns

Le piège le plus fréquent lors d'une migration Angular 18 → 19 est de mélanger les deux approches : garder Zone.js mais utiliser des signaux, ou hydrater partiellement sans corriger les mismatches. Cela crée une zone grise où les performances se dégradent et les bugs deviennent imprévisibles. Décidez clairement : soit vous gardez Zone.js et migrez graduellement, soit vous la retirez d'un coup en testant exhaustivement. Un autre piège : les libraries tierces qui dépendent de Zone.js. Si vous utilisez une librairie d'animation ou de formulaires qui s'appuie sur Zone.js, retirer Zone.js cassera ces dépendances. Vérifiez la compatibilité avant de commencer la migration (consultez les changelogs des dépendances ou ouvrez une issue sur GitHub).

Un dernier piège, souvent invisible : les performances en développement qui s'effondrent. Sans Zone.js et avec une mauvaise hydratation, le client peut déclencher des re-renders constants. Utilisez Chrome DevTools (Performance tab) pour identifier les repaint coûteux. Activez aussi Angular DevTools et regardez la fréquence des changements détectés (onglet Profiler). Si c'est un déluge, c'est que vous avez oublié un takeUntilDestroyed() ou qu'un observable fire en continu.

Checklist de migration

Pour migrer sereinement : (1) Créez une branche dédiée et mettez à jour ng update @angular/core @angular/cli. (2) Vérifiez les avertissements de déprépréciation avec ng build --configuration development. (3) Testez l'appli sans Zone.js en parallèle en gardant l'ancienne version en réserve. (4) Activez la nouvelle hydratation SSR et corrigez les mismatches. (5) Migrez les validateurs un module à la fois. (6) Lancez les tests e2e (Cypress, Playwright) pour détecter les comportements cachés. (7) Comparez les performances (bundle size, Lighthouse, Core Web Vitals) avant et après.

La migration Angular 18 → 19 n'est pas complexe, mais elle demande de la méthode. Les ruptures ne sont pas des bugs, ce sont des évolutions mûres qui consolident la plateforme. Prenez le temps de comprendre chaque changement, testez progressivement, et vous sortirez avec une app plus performante, plus facile à maintenir, et prête pour l'avenir.

Développeur Angular & Mobile freelance — Strasbourg.

© 2026 Emilien Pons — Tous droits réservés.Conçu avec Angular, PrimeNG et ❤️