Abandonnez Zone.js avec Angular 18 : comprenez le mode zoneless, configurez provideExperimentalZonelessChangeDetection et boostez les performances.
Le problème de Zone.js
Depuis la naissance d'Angular, Zone.js est la bibliothèque qui "patche" les APIs asynchrones du navigateur (setTimeout, Promises, XHR, etc.) pour déclencher automatiquement la détection de changements après chaque opération asynchrone.
Si Zone.js fonctionne bien, il comporte des inconvénients :
- Bundle size : ~150 KB ajoutés à chaque application
- Overhead : chaque opération asynchrone déclenche potentiellement une détection de changements complète
- Débogage : les stack traces sont polluées par Zone.js
- Compatibilité : certaines APIs modernes (Signals, streams) ne fonctionnent pas bien avec le patching
Principe du mode zoneless
En mode zoneless, Angular ne patch plus les APIs asynchrones. La détection de changements se déclenche uniquement quand :
- Un signal change de valeur
- Un composant appelle explicitement
markForCheck() - Un event handler DOM est déclenché dans un composant Angular
- Un
async pipeémet une nouvelle valeur
Cela signifie qu'Angular ne vérifie que les composants qui ont réellement changé, et non l'arbre entier à chaque tick.
Configuration dans Angular 18
Pour activer le mode zoneless, modifiez votre app.config.ts :
import { ApplicationConfig } from '@angular/core';
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
// ... autres providers
]
};
Ensuite, retirez Zone.js du polyfills dans angular.json :
// angular.json
{
"projects": {
"my-app": {
"architect": {
"build": {
"options": {
"polyfills": [] // Supprimer "zone.js"
}
}
}
}
}
}
Experimental). Il devient stable progressivement dans Angular 19 et 20.
Pourquoi les signaux sont indispensables
Sans Zone.js, Angular ne sait plus "automatiquement" quand l'état a changé. Les signaux deviennent le mécanisme principal de notification :
@Component({
selector: 'app-counter',
standalone: true,
template: `
<p>Compteur : {{ count() }}</p>
<button (click)="increment()">+1</button>
`
})
export class CounterComponent {
count = signal(0);
increment() {
this.count.update(v => v + 1);
// Angular détecte le changement via le signal
// Pas besoin de Zone.js
}
}
Quand count.update() est appelé, Angular sait exactement quel composant a besoin d'être mis à jour — uniquement CounterComponent.
markForCheck() et ChangeDetectorRef
Pour les composants qui n'utilisent pas de signaux (code legacy, bibliothèques tierces), markForCheck() reste disponible :
@Component({ ... })
export class LegacyComponent {
data: any;
constructor(
private service: DataService,
private cdr: ChangeDetectorRef
) {}
ngOnInit() {
this.service.getData().subscribe(result => {
this.data = result;
this.cdr.markForCheck(); // notifie Angular manuellement
});
}
}
Gains de performances
Les applications en mode zoneless avec signaux montrent des améliorations mesurables :
- Bundle initial : -150 KB (Zone.js supprimé)
- Temps de démarrage : réduit car aucun patching des APIs au bootstrap
- Détection de changements : granulaire au niveau du signal, pas de check global
- Memory : moins d'allocations liées au patching des Promises/XHR