0 Tage Serie
javascript / expert
Snippet

Zone-lose Änderungserkennung mit Signal-gesteuerten Komponentengrenzen

Ohne Zone.js patcht Angular nicht mehr jede asynchrone API (setTimeout, DOM-Events, Promises), um automatisch eine Änderungserkennung auszulösen — `provideExperimentalZonelessChangeDetection` entfernt dieses globale Monkey-Patching vollständig. Die Änderungserkennung wird stattdessen nur dann eingeplant, wenn sich der Wert eines im Template gelesenen Signals tatsächlich ändert, nachverfolgt über den reaktiven Graphen, den `computed` und `signal` aufbauen. Jede Zustandsänderung, die Signals umgeht — etwa das Mutieren einer einfachen Objekteigenschaft in der Erwartung, die View bemerke es —, wird für die View dauerhaft unsichtbar, da keine Zone mehr als Auffangnetz bereitsteht, und genau diese Abwägung muss man auf Expertenniveau in Angular explizit durchdenken.

snippet.js
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { bootstrapApplication } from '@angular/platform-browser';
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-price-badge',
standalone: true,
template: `<span>{{ formatted() }}</span>`,
})
export class PriceBadgeComponent {
private cents = signal(0);
formatted = computed(() =>
new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' })
.format(this.cents() / 100),
);
setCents(value: number): void {
this.cents.set(value);
}
}
bootstrapApplication(PriceBadgeComponent, {
providers: [provideExperimentalZonelessChangeDetection()],
});
angular
Erklärung
1
provideExperimentalZonelessChangeDetection()
Entfernt das Zone.js-Patching vollständig; die Änderungserkennung wird jetzt ausschließlich durch Signal-Lesungen im Template gesteuert, nicht durch abgefangene asynchrone APIs.
2
private cents = signal(0);
Ein beschreibbares Signal — die einzige Art von Zustandsänderung, die der zone-lose Scheduler tatsächlich beobachten und darauf reagieren kann.
3
formatted = computed(() => ...)
Ein abgeleitetes Signal, das nur bei einer Änderung von `cents` neu berechnet wird und das Template nur dann neu rendert, wenn sich sein eigener Ausgabewert unterscheidet.
4
setCents(value: number): void { this.cents.set(value); }
Der korrekte Mutationspfad in einer zone-losen App — `.set()` aufzurufen plant tatsächlich ein Re-Rendering ein; eine direkte Eigenschaftszuweisung würde das nicht tun.