Einführung
Seit Angular Version 18 besteht die Möglichkeit Angular-Apps „zoneless“ zu gestalten – was die Entwicklung vereinfachen und die Performance verbessern soll. Doch was genau bedeutet „zoneless“ eigentlich?
In diesem Blog-Beitrag schauen wir uns an, was Zone.js ist, welche Aufgabe Zone.js in Angular erfüllt und warum wir ein Interesse daran haben sollten, langfristig ohne Zone.js auszukommen.
Was ist Zone.js?
Zone.js ist eine vom Angular-Team gepflegte Bibliothek, die asynchrone Tasks abfängt. Im Quellcode heißt es: „Zone is a mechanism for intercepting and keeping track of asynchronous work. … In its simplest form a zone allows one to intercept the scheduling and calling of asynchronous operations, and execute additional code before as well as after the asynchronous task.“
Einfacher gesagt: Zone.js erweitert verschiedene asynchrone Browser-APIs durch sogenanntes „Monkey-Patching“ um eine Überwachungsfunktionalität. Das bedeutet, dass das Standardverhalten von APIs wiesetTimeout
, Promise
und DOM-Events so verändert wird, dass deren Ausführung mit Code ergänzt wird, der den Lebenszyklus jeder asynchronen Aufgabe überwacht.
Warum ist das für Angular relevant?
Angular ist darauf angewiesen, genau zu wissen, wann sich der Zustand einer Komponente ändert. Wird ein Signal (seit Angular V16) aktualisiert, „weiß“ Angular, welche spezifischen Komponenten davon abhängen, und rendert nur diese neu, während andere unberührt bleiben. Allerdings lassen sich nicht alle Interaktionen und Events, die potentiell Daten verändern könnten, direkt von Angular nachverfolgen. Dies gilt besonders für Applikationen, welche noch nicht auf Signals umgestiegen sind.
Hier kommt Zone.js ins Spiel.
Zone.js wird hauptsächlich dazu verwendet, automatisch die Change-Detection von Angular auszulösen nachdem bestimmte asynchrone Browser-Events aufgetreten sind, welche Angular nicht selbst direkt beobachten kann.
Zum Beispiel: Wenn einsetTimeout
-Callback abgeschlossen ist, würde Angular dies normalerweise nicht mitbekommen, da setTimeout
nicht über den eigenen Status informiert. Um dieses Problem zu lösen, müssten Entwickler:innen im Callback manuell die Change-Detection selbst auslösen.
Durch das Abfangen asynchroner Ereignisse wie HTTP-Anfragen und Timer kann Zone.js Angular über deren Zustand informiert halten und ermöglicht es, eine Change-Detection auszulösen, sobald ein solches Ereignis abgeschlossen ist. Auf diese Weise kann Angular auf asynchrone Ereignisse reagieren, ohne dass zusätzliche manuelle Eingriffe der Entwickler:innen notwendig sind.
Konkrete Beispiele
Die folgenden zwei konkreten Beispiele veranschaulichen die Vorteile von Zone.js. Beide enthalten einecounterComponent
, die eine primitive (nicht-Signal) Variable namens counter
verwendet, welche einmal pro Sekunde über ein setInterval
erhöht wird.
Im ersten Beispiel fängt Zone.js das asynchrone Ereignis ab, sodass Angular über die Ausführung informiert wird somit die Change-Detection wie erwartet automatisch ausgelöst wird.
Im zweiten Beispiel ist Zone.js deaktiviert, sodass die Ansicht nicht aktualisiert wird. Allerdings kann die Change-Detection hier manuell durch einen Button ausgelöst werden.
In einem realen Szenario könnte die Change-Detection natürlich auch direkt imsetInterval
ausgelöst werden. Diese Umsetzung dient dazu, die manuelle Natur des Problems zu verdeutlichen.
Warum versuchen wir Zone.js zu entfernen?
Obwohl Zone.js praktische Vorteile bietet, bringt es in modernen Angular-Anwendungen auch erhebliche Nachteile mit sich. Besonders durch Features wie Signals und Push-basierte Komponenten sinkt die Abhängigkeit zu Zone.js deutlich. Das Angular-Team bietet hier eine gute Übersicht darüber, warum eine zoneless-Anwendung sinnvoll ist: Why use Zoneless.
Hier eine Zusammenfassung:
-
Unnötige Change-Detection:
Zone.js unterscheidet nicht zwischen Events, welche eine Change-Detection erfordern, und solchen, die es nicht tun. Wenn beispielsweise ein Timer oder eine HTTP-Anfrage abgeschlossen ist, führt Angular die Change-Detection für die gesamte App immer zwingend durch, selbst wenn sich visuell nichts verändert hat. Diese zusätzliche Arbeit kann in großen, komplexen Apps zu Performance-Problemen führen.
Dieses Verhalten lässt sich visuell gut in der Demo von Jean Meche nachvollziehen. Sie stellt dar, wie die Angular Change-Detection Komponenten, welche nicht Push-basiert sind, immer neu rendert, selbst wenn keine Notwendigkeit hierfür besteht. - Komplexes Debugging: Zone.js fügt der Anwendung eine zusätzliche Komplexitätsebene hinzu, indem es unsichtbare Änderungen an Standard-APIs vornimmt. Dies kann Debugging erschweren, da Fehler im Zone.js-Kontext oft zu komplexen Stacktrace-Meldungen führen.
-
Einschränkungen bei modernen APIs:
Da Zone.js mit Monkey-Patching bestehender Browser-APIs arbeitet, kann es sein, dass moderne Features schlichtweg noch nicht in Zone.js abgebildet sind. Einige APIs wie
async/await
lassen sich zudem nur schwer oder gar nicht durch Zone.js patchen.
Wie lässt sich Zone.js schon heute entfernen?
Seit Angular Version 18 kann die Zone.js-Integration einfach deaktiviert werden, indem die Bootstrapping-Methode der Anwendung angepasst wird:
bootstrapApplication(App, { providers: [provideExperimentalZonelessChangeDetection()] });Zusätzlich muss
zone.js
aus dem Polyfills-Array in der angular.json
entfernt und die Abhängigkeit schließlich mit npm uninstall zone.js
deinstalliert werden.
Das war’s! 🎉
Damit alle Komponenten weiterhin wie erwartet funktionieren, sollten sie „Push-basiert“ sein, indemchangeDetection: ChangeDetectionStrategy.OnPush
gesetzt wird. Dies ist zwar keine zwingende Voraussetzung, sorgt jedoch für einen reibungslosen Betrieb ohne Zone.js.
Falls NgZone.onMicrotaskEmpty
und NgZone.onStable
verwendet werden, sollten diese durch Aufrufe zu afterNextRender
und afterRender
ersetzt werden.
Welche Probleme können auftreten, wenn Zone.js entfernt wird?
Nur weil die Komponenten zoneless funktionieren, bedeutet das nicht, dass auch die Anwendung und deren Abhängigkeiten vollständig darauf vorbereitet sind. Viele Bibliotheken sind möglicherweise veraltet und hängen noch von Zone.js-Features ab, um bestimmte Funktionalitäten bereitzustellen. Ein populäres Beispiel hierfür ist Material Angular.
Im Laufe des Jahres wurden viele Probleme im Zusammenhang mit zoneless-Deployments behoben, jedoch sind zum Zeitpunkt des Schreibens (November 2024) einige Probleme noch offen. Da jedoch schon viele Probleme bezüglich zoneless behoben wurden ist die Hoffnung groß, dass zum offiziellen, nicht-experimentellen, Release solche populären Bibliotheken reibungslos verwendbar sein werden.
Ausblick: Ein schnelleres, einfacheres Angular
Der Umstieg auf zoneless Angular könnte ein großer Gewinn für Performance und Einfachheit sein. Da das Angular-Ökosystem mit jedem Release neue Features wie Signals integriert, die für den zoneless-Betrieb ausgelegt sind, bieten zoneless Anwendungen spannende Vorteile für die moderne Angular-Entwicklung:
- Verringerte Komplexität: Durch das Entfernen von Zone.js wird versteckte Komplexität beseitigt. Dadurch wird die kognitive Belastung reduziert und das notwendige Hintergrundwissen verringert.
- Verbesserte Performance: Da die Change-Detection nur dann ausgelöst wird, wenn sie tatsächlich benötigt wird, werden Anwendungen reaktionsschneller und benötigen weniger Ressourcen.
Wer mit einer modernen Angular-Codebasis und aktualisierten Abhängigkeiten arbeitet, kann zoneless bereits heute ausprobieren und möglicherweise schon von den Vorteilen profitieren.