In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article introduces what are the onPush change detection strategies in Angular, the content is very detailed, interested friends can refer to, I hope it can be helpful to you.
Default change detection policy
By default, Angular uses the ChangeDetectionStrategy.Default policy for change detection.
The default policy does not make any assumptions about the application in advance, so change detection is performed in all components whenever user events, timers, XHR, promise, and other events change the data in the application.
This means that anything from a click event to data received from an ajax call triggers change detection.
We can easily see this by defining a getter in the component and using it in the template:
@ Component ({template: `Hello {{name}}! {{runChangeDetection}} `}) export class HelloComponent {@ Input () name: string; get runChangeDetection () {console.log ('Checking the view'); return true;}} @ Component ({template: `Trigger change detection`}) export class AppComponent {onClick () {}}
After executing the above code, whenever we click the button. Angular will run a change detection cycle, and we can see two lines of "Checking the view" logs in console.
This technique is called dirty inspection. In order to know whether the view needs to be updated, Angular needs to access the new value and compare it with the old value to determine whether the view needs to be updated.
Now imagine that if there were a large application with thousands of expressions and Angular checked each expression, we might encounter performance problems.
Is there any way for us to take the initiative to tell Angular when to check our components?
OnPush change detection strategy
We can set the ChangeDetectionStrategy of the component to ChangeDetectionStrategy.OnPush.
This will tell Angular that the component depends only on its @ inputs () and needs to be checked only in the following cases:
1. Input reference has changed
By setting the onPush change detection policy, we contracted with Angular to enforce the use of immutable objects (or observables, which will be described later).
The advantage of using immutable objects in the context of change detection is that Angular can determine whether the view needs to be checked by checking whether the reference has changed. This will be much easier than in-depth inspection.
Let's try to modify an object and see the results.
@ Component ({selector: 'tooltip', template: `{{runChangeDetection}}`, changeDetection: ChangeDetectionStrategy.OnPush}) export class TooltipComponent {@ Input () config; get runChangeDetection () {console.log (' Checking the view'); return true;}} @ Component ({template: `}) export class AppComponent {config = {position: 'top'} OnClick () {this.config.position = 'bottom';}}
You won't see any logs when you click the button at this time, because Angular compares the references to the old and new values, similar to:
/ * Returns false in our case * / if (oldValue! = = newValue) {runChangeDetection ();}
It is worth mentioning that numbers, booleans, strings, null, and undefined are primitive types. All primitive types are passed by value. Objects, arrays, and functions are also passed by value, but the value is a copy of the reference address.
So in order to trigger change detection for the component, we need to change the reference to the object.
@ Component ({template: ``}) export class AppComponent {config = {position: 'top'}; onClick () {this.config = {position:' bottom'}
After changing the object reference, we will see that the view has been checked and the new values are displayed.
two。 Events originating from this component or its child components
When an event is triggered in a component or its subcomponents, the internal state of the component is updated. For example:
@ Component ({template: `Add {{count}} `, changeDetection: ChangeDetectionStrategy.OnPush}) export class CounterComponent {count = 0; add () {this.count++;}}
When we click the button, Angular executes a change detection loop and updates the view.
You might think, as we said at the beginning, that every asynchronous API triggers change detection, but that's not the case.
You will find that this rule only applies to DOM events, and the following API does not trigger change detection:
@ Component ({template: `...`, changeDetection: ChangeDetectionStrategy.OnPush}) export class CounterComponent {count = 0; constructor () {setTimeout (() = > this.count = 5,0); setInterval (() = > this.count = 5,100); Promise.resolve (). Then (() = > this.count = 5); this.http.get ('this.count = > {this.count = res;}) } add () {this.count++;}
Note that you still update this property, so in the next change detection process, such as clicking a button, the count value will be 6 (5 percent 1).
3. To perform change detection as shown
Angular provides us with three ways to trigger change detection.
The first is detectChanges () to tell Angular to perform change detection in the component and its subcomponents.
@ Component ({selector: 'counter', template: `{{count}}`, changeDetection: ChangeDetectionStrategy.OnPush}) export class CounterComponent {count = 0; constructor (private cdr: ChangeDetectorRef) {setTimeout (() = > {this.count = 5; this.cdr.detectChanges ();}, 1000);}}
The second is ApplicationRef.tick (), which tells Angular to perform change detection for the entire application.
Tick () {try {this._views.forEach ((view) = > view.detectChanges ());...} catch (e) {...}}
The third is markForCheck (), which does not trigger change detection. Instead, it detects all ancestor tags with onPush set in the current or next change detection cycle.
MarkForCheck (): void {markParentViewsForCheck (this._view);} export function markParentViewsForCheck (view: ViewData) {let currView: ViewData | null = view; while (currView) {if (currView.def.flags & ViewFlags.OnPush) {currView.state | = ViewState.ChecksEnabled;} currView = currView.viewContainerParent | | currView.parent;}}
It is important to note that performing change detection manually is not a "hack", which is intentionally designed by Angular and is a very reasonable behavior (in a reasonable scenario, of course).
Angular Async pipe
Async pipe subscribes to an Observable or Promise and returns the most recent value it emits.
Let's see that input () is the onPush component of observable.
@ Component ({template: `Add `}) export class AppComponent {items = []; items$ = new BehaviorSubject (this.items); add () {this.items.push ({title: Math.random ()}) this.items$.next (this.items);}} @ Component ({template:` {item.title}} `, changeDetection: ChangeDetectionStrategy.OnPush}) export class ListComponent implements OnInit {@ Input () items: Observable; _ items: Item [] NgOnInit () {this.items.subscribe (items = > {this._items = items;});}}
When we click the button, we can't see the view update. This is because none of the above has happened, so Angular does not check the component during the current change detection cycle.
Now, let's try adding async pipe.
@ Component ({template: `{{item.title}}`, changeDetection: ChangeDetectionStrategy.OnPush}) export class ListComponent implements OnInit {@ Input () items;}
Now you can see that when we click the button, the view is updated. The reason is that when a new value is emitted, async pipe marks the component as having changed and needs to be checked. We can see it in the source code:
Private _ updateLatestValue (async: any, value: Object): void {if (async = = this._obj) {this._latestValue = value; this._ref.markForCheck ();}}
Angular calls markForCheck () for us, so we can see that the view has been updated even if the reference to the input has not changed.
If a component relies only on its input property, and the input property is observable, the component changes only when its input property emits an event.
Quick tip: it is not recommended to expose your subject externally. Always use the asObservable () method to expose the observable.
OnPush and View query @ Component ({selector: 'app-tabs', template: ``}) export class TabsComponent implements OnInit {@ ContentChild (TabComponent) tab: TabComponent; ngAfterContentInit () {setTimeout (() = > {this.tab.content =' Content';}, 3000);}} @ Component ({selector: 'app-tab', template: `{content}}, changeDetection: ChangeDetectionStrategy.OnPush}) export class TabComponent {@ Input () content;}
You might think that in 3 seconds Angular will update the tab component with the new content.
After all, we update the input reference to the onPush component, which triggers change detection, doesn't it?
In this case, however, it does not work. Angular does not know that we are updating the input property of the tab component, and defining input () in the template is the only way to let Angular know that this property should be checked in the change detection cycle.
For example:
Because when we explicitly define input () in the template, Angular creates a method called updateRenderer (), which tracks the value of content in each change detection cycle.
The simple solution in this case is to use setter and then call markForCheck ().
@ Component ({selector: 'app-tab', template: `{{_ content}}`, changeDetection: ChangeDetectionStrategy.OnPush}) export class TabComponent {_ content; @ Input () set content (value) {this._content = value; this.cdr.markForCheck ();} constructor (private cdr: ChangeDetectorRef) {} = = onPush++
After understanding the power of onPush, let's use it to create a higher performance application. The more onPush components, the less checks Angular needs to perform. Let's take a look at a real example of you:
We have another todos component that has a todos as input ().
@ Component ({selector: 'app-todos', template: `{{todo.title}}-{{runChangeDetection}}`, changeDetection: ChangeDetectionStrategy.OnPush}) export class TodosComponent {@ Input () todos; get runChangeDetection () {console.log (' TodosComponent-Checking the view'); return true } @ Component ({template: `Add`}) export class AppComponent {todos = [{title: 'One'}, {title:' Two'}]; add () {this.todos = [... this.todos, {title: 'Three'}];}}
The disadvantage of the above approach is that when we click the add button, Angular needs to check each todo even if there is no change in the previous data. So after the first click, three logs are displayed in the console.
In the above example, there is only one expression to check, but imagine a real component with multiple bindings (ngIf,ngClass, expressions, and so on), which can be very performance-consuming.
We performed change detection in vain!
A more efficient approach is to create a todo component and define its change detection policy as onPush. For example:
@ Component ({selector: 'app-todos', template: ``, changeDetection: ChangeDetectionStrategy.OnPush}) export class TodosComponent {@ Input () todos;} @ Component ({selector:' app-todo', template: `{{runChangeDetection}}`, changeDetection: ChangeDetectionStrategy.OnPush}) export class TodoComponent {@ Input () todo; get runChangeDetection () {console.log ('TodoComponent-Checking the view'); return true;}}
Now, when we click the add button, we will see only one log in the console, because the input of the other todo components has not changed, so we will not check their views.
And, by creating smaller-grained components, our code becomes more readable and reusable.
This is the end of the onPush change detection strategy in Angular. I hope the above content can be helpful to you and learn more. If you think the article is good, you can share it for more people to see.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.