Seit über einem Jahrzehnt hat Angular ein grundlegendes Problem gelöst:wann die Benutzeroberfläche erneut überprüft werden sollte, nachdem sich etwas geändert hat– mit einer ebenso genialen wie invasiven Lösung:Zone.js, eine Bibliothek, die die asynchrone API des Browsers zur Laufzeit neu schreibt, um mögliche Änderungsursachen abzufangen. Mit der Ankunft vonSignaleAngular gibt diesen Ansatz nach und nach auf und setzt auf ein Modell, bei dem die Daten selbst und nicht ein globaler Monkey-Patch wissen, wer aktualisiert werden muss. In diesem Artikel wird erläutert, wie Zone.js intern funktioniert, warum sein Modell unlösbare strukturelle Einschränkungen aufweist und was die Erstellung einer Angular-Anwendung in der Praxis bedeutetzonenlos.
So funktioniert die Zone.js-basierte Änderungserkennung
Zone.js löst ein sehr reales Problem: Angular muss es wissenWannÜberprüfen Sie Komponenten erneut, um das DOM zu aktualisieren, aber JavaScript bietet nativ keine Möglichkeit, zu „beobachten“, wenn sich ein Wert auf generische Weise ändert. Die Lösung von Zone.js ist drastisch: Beim Bootstrapping der Anwendungüberschreibt(Monkey-Patch) die globalen asynchronen Browser-APIs –setTimeout,setInterval,addEventListener,Versprechen,XMLHttpRequest– so dass Zone.js jedes Mal, wenn eine dieser APIs einen Vorgang abschließt, diesen abfängt und Angular benachrichtigt.
Der „weit verbreitete“ Änderungserkennungszyklus
Wenn Zone.js ein Ereignis benachrichtigt, weiß Angular nichts davonwas genauer hat sich verändert – das weiß er einfachetwas, irgendwo, es könnte sich geändert haben. Die Antwort besteht darin, den gesamten Komponentenbaum erneut von oben nach unten zu überprüfen (Dirty Checking) und dabei die Werte in den Vorlagen mit den zuvor gerenderten zu vergleichen:
// Semplificato: cosa succede concettualmente dopo OGNI evento asincrono
zone.onMicrotaskEmpty.subscribe(() => {
applicationRef.tick(); // ricontrolla l'intero albero dei componenti
});
Aus diesem Grund ist es historisch gesehen eine Singleklickenauf eine Schaltfläche in einer Ecke der Anwendung kann dazu führen, dass völlig unabhängige Komponenten an anderer Stelle im Baum erneut überprüft werden –ChangeDetectionStrategy.OnPushmildert das Problem (überspringt Teilbäume, deren@Eingang()werden nicht durch Referenz geändert), aber es beseitigt es nicht: Zone.js löst weiterhin eine Kontrollschleife für jedes einzelne asynchrone Ereignis aus, unabhängig davon, ob es zu einer tatsächlichen Änderung führt oder nicht.
Die strukturellen Grenzen von Zone.js
Zone.js hat als „Einheitslösung“ gut funktioniert, sein Ansatz weist jedoch Probleme auf, die nicht mit inkrementellen Optimierungen gelöst werden können:
1. Bundle- und Laufzeit-Overhead
Zone.js wiegt im ersten Bundle etwa 30–35 KB (minimiert) – ein Fixpreis für jede Angular-Anwendung, unabhängig davon, wie oft sie ihre Funktionalität tatsächlich nutzt. Zur Laufzeit führt das Monkey-Patching jeder asynchronen API zu messbarem Overhead bei jedem einzelnen Aufruf, selbst wenn es zu keiner wirklichen Änderung der Benutzeroberfläche führt.
2. Nicht-selektive Änderungserkennung
Zone.js weiß esDasEtwas ist passiert, aber er weiß es nichtWas. Das Ergebnis ist ein Regelkreis, der in den meisten Fällen weit mehr überprüft, als nötig ist – sogar mitOnPushObwohl es überall aktiv ist, löst jedes asynchrone Ereignis dennoch eine Überprüfungsrunde für den gesamten potenziell beteiligten Baum aus.
3. Fragilität bei Bibliotheken von Drittanbietern
Globales Monkey-Patching von APIs wieVersprechenoderbringenkann unvorhersehbar mit Bibliotheken interagieren, die nicht erwarten, dass diese APIs zur Laufzeit neu geschrieben werden – dies ist eine häufige und schwer zu diagnostizierende Ursache für zeitweilige Fehler in großen Angular-Anwendungen.
4. Komplexeres Debugging
Stapelspuren durchqueren den Zone.js-Patchcode, wodurch es schwieriger wird, die wahre Ursache eines asynchronen Fehlers zu ermitteln – jeden, der einen Fehler behoben hatUnhandledPromiseRejectionin einer großen Angular-App kennt dieses Problem.
Was die Signale verändern
Signals kehrt das Modell um: Statt „Irgendwo ist etwas passiert, überprüfe alles noch einmal“, wissen die Daten selbst Bescheidgenauwer von ihm abhängig ist, denn die Abhängigkeit wird zum Zeitpunkt der Lektüre explizit erfasst:
import { signal, computed, effect } from '@angular/core';
const count = signal(0);
const doubled = computed(() => count() * 2); // dipendenza tracciata automaticamente
effect(() => {
console.log('Il valore doppio è', doubled());
// questo effect si ri-esegue SOLO quando doubled() cambia realmente,
// non ad ogni evento asincrono dell'applicazione
});
count.set(5); // notifica solo i consumer reali: doubled, e l'effect
Wanncount.set(5)heißt, Angular weiß genau welcheberechnet, welcheWirkungund von welchen Vorlagenbindungen sie abhängenzählen– Es ist nicht erforderlich, den gesamten Komponentenbaum noch einmal zu überprüfen, da der Abhängigkeitsgraph bereits im Voraus bekannt ist.
Zone.js vs. Signals: direkter Vergleich
| Ich warte | Zone.js (klassische Änderungserkennung) | Signale |
|---|---|---|
| Wie es Änderungen erkennt | Fangen Sie jede globale asynchrone API ab | Explizite Verfolgung von Leseabhängigkeiten |
| Was überprüfen Sie noch einmal? | Der gesamte Baum (oder Teilbaum mit OnPush) | Nur Verbraucher, die tatsächlich abhängig sind |
| Bündel-Overhead | ~30-35 KB behoben | Im Kern enthalten, keine zusätzlichen Kosten |
| Kompatibilität mit Bibliotheken von Drittanbietern | Konfliktgefahr durch Monkey-Patching | Kein globales Patching erforderlich |
| Vorhersagbarkeit debuggen | Stack-Traces-Cross-Patching | Expliziter und nachvollziehbarer Abhängigkeitsfluss |
| Akzeptanzkurve | Es funktioniert vom ersten Tag an „kostenlos“. | Erfordert eine Umgestaltung des bestehenden Zustands |
Was bedeutet „Winkelzonenlos“ in der Praxis?
Winkelzonenlos bedeutet nicht nur „Signale in Komponenten verwenden“ – es bedeutetEntfernen Sie Zone.js vollständigaus dem Bündel und vertrauen Sie die gesamte Änderungserkennung dem Signal-Reaktivitätsdiagramm an. Die Aktivierung erfolgt bei einem dedizierten Anbieter während der Bootstrap-Phase:
import { bootstrapApplication } from '@angular/platform-browser';
import { provideZonelessChangeDetection } from '@angular/core';
bootstrapApplication(AppComponent, {
providers: [
provideZonelessChangeDetection(),
// ...altri provider
],
});
Sobald Zone.js entfernt wurde, verfügt Angular nicht mehr über die „automatische“ Möglichkeit, eine generische Änderung zu bemerken –Alles ist in OrdnungDer Zustand, der in der Benutzeroberfläche widergespiegelt werden muss, muss explizit ein Signal (oder eine) durchlaufenChangeDetectorRef.markForCheck()im Extremfall das Handbuch). Aus diesem Grund ist Migration kein einfach zu aktivierendes Flag, sondern ein echter Paradigmenwechsel in der Staatsführung.
Praktischer Leitfaden: Migration einer Anwendung auf zonenlos
1. Überprüfen Sie Ihre OnPush-Abdeckung
Wenn die Anwendung weiterhin verwendet wirdChangeDetectionStrategy.DefaultIn vielen Komponenten ist es das erste Anzeichen dafür, dass der Zustand nicht explizit verwaltet wird – übergeben Sie alles anOnPushDies ist eine praktische Voraussetzung, bevor Zone.js überhaupt entfernt wird.
2. Ersetzen Sie den „impliziten“ Zustand durch Signale
// Prima: proprietà di classe normale, aggiornata via mutazione diretta
export class CartComponent {
itemCount = 0;
addItem(): void {
this.itemCount++; // senza Zone.js, la UI non si aggiornerebbe
}
}
// Dopo: signal, aggiornamento esplicito e tracciato
export class CartComponent {
itemCount = signal(0);
addItem(): void {
this.itemCount.update(n => n + 1); // il template si aggiorna automaticamente
}
}
3. Überprüfen Sie die Bibliotheken von Drittanbietern
Einige Bibliotheken (insbesondere ältere Komponenten von Drittanbietern oder Code, der implizit das Vorhandensein von Zone.js annimmt, um Angular nach einem Ereignis „aufzuwecken“) aktualisieren die Benutzeroberfläche im zonenlosen Modus möglicherweise nicht mehr ordnungsgemäß. Sie müssen einzeln getestet oder in einem verpackt werdenmarkForCheck()Handbuch an den Integrationspunkten.
4. Entfernen Sie Zone.js aus dem Bundle
Sobald überprüft wurde, dass alle Zustände Signale durchlaufen (oder explizite manuelle Änderungserkennung), kann Zone.js aus der Abhängigkeits- und Polyfill-Datei entfernt werden, wodurch das anfängliche Paket reduziert wird.
Wenn es noch NICHT bequem ist, zonenlos zu arbeiten
- Anwendungen mit vielen veralteten Abhängigkeiten von Drittanbietern: Wenn kritische Bibliotheken das Vorhandensein von Zone.js voraussetzen, erfordert die Migration umfangreiche Einzelfalltests.
- Sehr große Codebasen mit ungleichmäßig verwaltetem Status: Wenn der Status zwischen direkt mutierten Klasseneigenschaften, Diensten mit RxJS, die nicht in Signals integriert sind, und Legacy-Logik verstreut ist, ist die erforderliche Umgestaltung erheblich.
- Teams, die mit Signalen nicht vertraut sind: Zoneless vergrößert alle Lücken in der expliziten Zustandsverwaltung – es lohnt sich, zunächst Ihr Signalwissen zu festigen, da Zone.js immer noch als Sicherheitsnetz aktiv ist.
Häufig gestellte Fragen
Muss ich Zone.js entfernen, um Signale zu verwenden?
Nein, Signale funktionieren einwandfrei, auch wenn Zone.js noch aktiv ist – es ist der häufigste Zwischenschritt: Zuerst Signale für den Status übernehmen und dann (optional) Zone.js entfernen, wenn die Abdeckung abgeschlossen ist.
Ersetzen Signale RxJS in Angular?
Nein, sie decken unterschiedliche Anwendungsfälle ab: Signale sind für den synchronen Zustand lokaler Komponenten konzipiert, RxJS bleibt das richtige Werkzeug für komplexe asynchrone Abläufe (HTTP-Anfragen, WebSockets, Kombination mehrerer Ereignisse im Zeitverlauf). Die VersorgungsunternehmentoSignal()UndtoObservable()ermöglichen die Koexistenz der beiden Modelle.
Ist Zoneless schon produktionsreif?
Die zonenlose Unterstützung ist in neueren Versionen von Angular stabil, erfordert jedoch, dass die gesamte Anwendung (einschließlich der verwendeten Bibliotheken von Drittanbietern) mit einem expliziten Änderungserkennungsmodell kompatibel ist – sie muss von Fall zu Fall bewertet werden, es handelt sich nicht um einen einfachen universellen Schalter.
Was passiert, wenn ich vergesse, ein Signal für einen Staat zu verwenden, der die Benutzeroberfläche aktualisieren muss?
Im zonenlosen Modus wird die Benutzeroberfläche einfach nicht aktualisiert, bis ein anderer Änderungserkennungsauslöser auftritt (z. B. ein Vorlagenereignis) – es handelt sich um einen stillen Fehler, weshalb die Migration umfangreiche Tests für jeden Flow erfordert.
Verbessern Signale die Leistung, auch ohne auf Zonen zu verzichten?
Ja: Auch wenn Zone.js noch aktiv ist, ermöglichen signalbasierte Bindungen in Vorlagen Angular, nur die tatsächlich abhängigen DOM-Knoten zu aktualisieren, wodurch der Rendering-Aufwand im Vergleich zu klassischen Bindungen reduziert wird.
Muss ich die gesamte Anwendung neu schreiben, um Signale zu übernehmen?
Nein, Signale und „klassischer“ Status können in derselben Komponente und Anwendung nebeneinander existieren – Sie können inkrementell, Komponente für Komponente, migrieren.
Zusammenfassend
Zone.js hat es Angular seit dem ersten Tag ermöglicht, die automatische Änderungserkennung „kostenlos“ anzubieten, aber der Preis ist ein Modell, das viel mehr überprüft, als es benötigt, mit festen Bundle-Kosten und einer bekannten Fragilität bei der Integration mit Bibliotheken von Drittanbietern. Signals löst das Problem an der Wurzel, indem es Abhängigkeiten explizit verfolgt, anstatt jedes asynchrone Browserereignis abzufangen – und ebnet den Weg für ein zonenloses Angular, das leichter und vorhersehbarer beim Debuggen ist. Wenn Sie darüber nachdenken, wann Sie Signals im Vergleich zu RxJS in der täglichen Statusverwaltung verwenden sollten, finden Sie in diesem Leitfaden einen direkten Link zuWinkelsignale vs. RxJS: Wann man das eine und wann das andere verwendet.