0 Tage Serie
javascript / expert
Snippet

Angulars Change-Detection-Kosten mit einer eigenen Profiling-Direktive diagnostizieren

Anders als ngOnChanges, das nur feuert, wenn sich eine @Input-Referenz tatsächlich ändert, läuft ngDoCheck bei jedem einzelnen Change-Detection-Zyklus, der eine Komponente erreicht — unabhängig davon, ob sich etwas Relevantes geändert hat. Das macht es zu einer präzisen, abhängigkeitsfreien Sonde, um herauszufinden, welche Teilbäume unter der Standard-Change-Detection (ohne OnPush) weit häufiger geprüft werden als erwartet. Wird diese Direktive während der Entwicklung an verdächtige Elemente gehängt, deckt sie Hotspots auf, die durch instabile Bindings entstehen — etwa einen Funktionsaufruf in einem Template-Ausdruck, der bei jedem Zyklus eine neue Objektreferenz erzeugt — ohne dass der Angular-Profiler der Browser-Devtools oder Drittanbieter-Instrumentierung nötig wären. Der visuelle rote Rahmen macht aus einer abstrakten Check-Zähler-Zahl etwas, das ein Entwickler buchstäblich auf dem Bildschirm aufblitzen sieht, während er mit der App interagiert.

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
25
26
27
28
29
30
31
import { Directive, inject, ElementRef, DoCheck, Input } from '@angular/core';
@Directive({
selector: '[cdProfiler]',
standalone: true,
})
export class CdProfilerDirective implements DoCheck {
@Input('cdProfiler') label = 'unlabeled';
private readonly el = inject(ElementRef<HTMLElement>);
private checkCount = 0;
private lastLogTime = 0;
// DoCheck fires on EVERY change detection pass touching this host,
// even when no binding actually changed -- unlike ngOnChanges.
ngDoCheck(): void {
this.checkCount++;
const now = performance.now();
if (now - this.lastLogTime > 2000) {
console.warn(
`[cdProfiler:${this.label}] ${this.checkCount} checks in ` +
`the last ${((now - this.lastLogTime) / 1000).toFixed(1)}s`
);
this.el.nativeElement.style.outline =
this.checkCount > 50 ? '2px solid red' : '';
this.checkCount = 0;
this.lastLogTime = now;
}
}
}
angular
Erklärung
1
ngDoCheck(): void {
Klinkt sich in den Low-Level-Lifecycle-Callback ein, der buchstäblich bei jedem Change-Detection-Durchlauf feuert.
2
this.checkCount++;
Zählt Prüfungen pro Host-Element, um zu quantifizieren, wie 'heiß' dieser Teilbaum im Vergleich zu anderen ist.
3
if (now - this.lastLogTime > 2000) {
Drosselt die Ausgabe, um die Konsole nicht zu fluten, da ngDoCheck dutzende Male pro Sekunde feuern kann.
4
this.el.nativeElement.style.outline = this.checkCount > 50 ? '2px solid red' : '';
Wandelt einen Zahlen-Schwellenwert in ein sofortiges visuelles Signal direkt am profilierten Element um.