Some colleagues who have not yet dealt much with the topic of ChangeDetection in Angular may find it difficult to imagine what the
difference between the default and the
OnPush approach is.
When your application gets larger this topic gets more relevant tou you.
Here I would like to compare and show how ChangeDetection can have an effect on the performance of an application.
What is ChangeDetection?
ChangeDetection in Angular is a process which check if components have to rerender.
By Default, each component is permanently checked whether values have changed.
In my main project which grew over the years it is a nearly endless type of processing.
So the idea was, step by step migrate to
But what is the difference?
OnPush it’s up to you to trigger the ChangeDection yourself, using the available tools.
In this articls I hope to give you a better overview of the differences and why it is important to not ignore that topic.
Before to start
what is the difference between
markForCheck should always be used if you have to signal the detector to check that view.
detectChanges should be used to check the view and the children of it.
If you want to check only a small scope of your detection tree, use
Warning: detach components should be done with caution!
Default: Angular will rather check the DOM tree too many times than not enough.
OnPush: In Angular it’s your own responsibility to use build-in capabilities like Pipes, Components and the ChangeDetectorRef to define where checks are necessary outside the lifecycle routines.
Now I going to use the following two components to build my test angular component tree.
AppComponent - the App Wrapper
DetectionBlockComponent - Components which create recursive Child Components.
My Component tree looks like:
| App | - 1 | - 3 | - 4 | - 5 | - 6 | - 2 | - 7 | - 8
The following steps I clicked in the appliation:
- Open Page
- click detectChanges 4
- click markForCheck 7
- click markForCheck 4
- click detectChanges 2
- click markForCheck App
- click detectChanges App
app in default detection
We come to the following numbers
the complete tree will be check from the detector on initial.
After the initial check, the app component in default mode will check multiple times itself and the children of it.
When click on
detectChanges of a node in the tree, itself and its children will be checked. If you don’t use
detach the ChangeDetection will start at the top of the tree and check until the origin of the detection event.
And again, default, app will check their children multiple times.
What happens with
- checks 7
- checks app
- checks 1
- checks 2
- checks 7
- checks 8
- double checks app again
app in onpush detection
We come to the following numbers
Wow! Just app component onPush v.s. default performed better by about 45%!
In initial check, ‘1’ and ‘2’ won’t check twice, only app still check multiple times in that setup.
Also additional steps won’t check as often.
App is more stable and won’t check the tree everytime so “aggressive”.
What’s the pitfall here? Mutations in objects without trigger detection by yourself can occure stale views. So be careful how you write component logic. Best practices IMHO are observables with async subscription in the template or call
markForCheck after async processes in the component.
detach components after view init
This section is more experimental! In this case, the simple application setup made it possible to receive the same application behavior then before. Larger apps may get more stale and shaky when detach without caution!
When you have call
detach the components after intial rendering show the following numbers
Awesome, from 83 to 38 is a lot of circles which we safed!
What’s the reason?
By detaching all components from the ChangeDetection, you take over the full control over the ChangeDetection of them.
For example, after ‘initial’ step the
detectChanges of ‘4’ will only effect app, ‘4’ and ‘5’.
‘5’ is the child, ‘4’ itself and app because it’s the root node and have to check how much of the total tree (which nomore exists more or less) has to check like before.
The reason for multiple checks of app is not clear to me, I’ll check it later.
What are the risks with this setup?
- Detach can hide changes in the detector circles, so changes in your component won’t notify the renderer and the DOM becomes stale.
- You have to do a lot of things manually, like detection and detach(reattach) the component
- Child components may not render or act correctly. In my first experiments with
MatInputit won’t display correct.
My learning while writing this post
detachstatic “leaf” components can safe performance
OnPushcan be hard but is essential for larger applications
Numbers behind the tests
About the author: Nils Heinemann
Nils has more than five years of experience in software development and architecture. He specializes in frontend development with Angular and TypeScript. Nils joined MaibornWolff in 2018.