<link rel="stylesheet" href="/assets/fonts/jetbrains-mono/jetbrains-mono.css" />
All posts

Angular Signals vs. Zone.js: Warum Angular zonenlos wird

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 warteZone.js (klassische Änderungserkennung)Signale
Wie es Änderungen erkenntFangen Sie jede globale asynchrone API abExplizite 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 behobenIm Kern enthalten, keine zusätzlichen Kosten
Kompatibilität mit Bibliotheken von DrittanbieternKonfliktgefahr durch Monkey-PatchingKein globales Patching erforderlich
Vorhersagbarkeit debuggenStack-Traces-Cross-PatchingExpliziter und nachvollziehbarer Abhängigkeitsfluss
AkzeptanzkurveEs 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.

💬 Leser-Notizen

1 Notiz

Notiz schreiben

Teile deine Meinung, einen Vorschlag oder ein Kompliment

Neueste Notizen

Matteo
l&#x27;ho letto con attenzione, un riassunto abbastanza preciso.