OnPush change detection strategy in Angular
Wojciech Trawiński
YouGov Front-end meeting, 2021
Angular performance tips
Angular performance tips
- OnPush change detection strategy,
- modules and components lazy-loading,
- taking control over change detection process,
- pure pipes,
- memoization,
- setters vs ngOnChanges
Change detection
Change detection
NgZone
Change detection
Default strategy
NgZone
Change detection
Default strategy
OnPush strategy
NgZone
Change detection
Default strategy
OnPush strategy
NgZone
Asynchronous events
OnPush strategy triggerers
OnPush strategy triggerers
- Input property change (compared by reference),
- event dispatched in the DOM sub-tree (with listener),
- markForCheck method's call (ChangeDetectorRef).
Immutability
Immutability
mutable update pattern
interface User {
name: string;
age: number;
}
...
public user: User = { name: "Max", age: 30 };
...
public changeUserAge(age: number): void {
this.user.age = age;
}
Immutability
mutable update pattern
immutable update pattern
interface User {
name: string;
age: number;
}
...
public user: User = { name: "Max", age: 30 };
...
public changeUserAge(age: number): void {
this.user.age = age;
}
interface User {
name: string;
age: number;
}
...
public user: User = { name: "Max", age: 30 };
...
public changeUserAge(age: number): void {
this.user = { ...this.user, age };
}
Asynchronous events
Asynchronous events
timers
public date: string = null;
private dateInterval = null;
...
public showDate(): void {
this.dateInterval = setInterval(() => {
this.date = new Date().toISOString();
}, 1000);
}
Asynchronous events
timers
XHR
public date: string = null;
private dateInterval = null;
...
public showDate(): void {
this.dateInterval = setInterval(() => {
this.date = new Date().toISOString();
}, 1000);
}
public remoteData: string = null;
...
public loadData(): void {
mockResponse$.subscribe(res => {
this.remoteData = res;
});
}
AsyncPipe
AsyncPipe
- automatically (un)subscribes to(from) stream,
- handles stream's reference change,
- calls the markForCheck method,
- does not apply distinctUntilChanged operator.
AsyncPipe
- automatically (un)subscribes to(from) stream,
- handles stream's reference change,
- calls the markForCheck method,
- does not apply distinctUntilChanged operator.
AsyncPipe
export function select<T, Props, K>(
pathOrMapFn: ((state: T, props?: Props) => any) | string,
propsOrPath?: Props | string,
...paths: string[]
) {
return function selectOperator(source$: Observable<T>): Observable<K> {
let mapped$: Observable<any>;
...
return mapped$.pipe(distinctUntilChanged());
};
}
with NgRx selectors
ngDoCheck method
ngDoCheck method
- top-level component with OnPush strategy,
- called frequently,
- manual checks and the markForCheck method call.
ngDoCheck method
- top-level component with OnPush strategy,
- called frequently,
- manual checks and the markForCheck method call.
Other gotchas
Other gotchas
- events outside of the NgZone,
- custom form controls,
- binding updates is a part of the change detection process.
OnPush by default
OnPush by default
{
...
"schematics": {
"@schematics/angular:component": {
"changeDetection": "OnPush"
}
},
...
}
angular.json
Conclusions
Conclusions
- favour immutable update patterns,
- subscribe using the AsyncPipe,
- prevent unnecessary updates (distinctUntilChanged),
- call markForCheck/detectChanges method as the last resort,
- bindings get updated as a part of the change detection.
OnPush change detection strategy in Angular
By wojtrawi
OnPush change detection strategy in Angular
- 848