With the increase and nesting of sub-components, the problem of data changes, but the component pages are not rendered, begins to increase.
E.g:
This page is a subcomponent. After opening the page, the existing attachments should be displayed, but now they are empty.
Display correctly:
The problem shows: no rendering
This problem often occurs because the change detector did not detect the data change of the component and did not perform change detection.
In the end, why it was not detected, and after exploring for a period of time, the root cause of the problem has not been explored.
However, it can often be solved by manual change detection. For example, in line 9 of the following code, the detectChanges function of ChangeDetectorRef is called . Today I will talk about the relevant knowledge about ChangeDetectorRef.
1 constructor(private attachmentService: AttachmentService,
2 private ref: ChangeDetectorRef) {
3 }
4 getAttachmentByIds() {
5 this.attachmentService.getAttachmentByIds(this.attachmentIds)
6 .subscribe(attachments => {
7 this.attachments = attachments;
8 // 进行强制变更检测 防止页面不显示attachments的变更
9 this.ref.detectChanges();
10 })
11 }
change detection
Let's first talk about the two strategies of angular change detection.
-
Default
-
OnPush
DefaultStrategy
Angular components can rely on other components to build the page logic of the application, and finally form a component tree. Each component has its own change detector. Therefore, the structure of the change detector is also an isomorphic tree
When the state of a component changes, Angular will traverse from the root node of the tree and start the change detector of all component nodes, so that Angular knows that the state of those components has changed and the corresponding UI needs to be updated.
This process may seem expensive, but Angular has done a lot of optimization and the actual change detection is fast. This strategy will have a performance impact on our application when there are too many components in our application, but the Default strategy is our best choice when we are not familiar with the relevant details.
OnPush strategy
I haven't tried the OnPush strategy yet, but it can be used as an understanding.
Angular also provides an OnPush strategy, we can modify the changeDetection
attribute of the component decorator to change the change detection strategy . As in line 4 of the code below.
1 @Component({
2 selector: 'app-A',
3 // 设置变化检测的策略
4 changeDetection: ChangeDetectionStrategy.OnPush,
5 template: ...
6 })
7 export class AComponent {
8 ...
9 }
Under the OnPush
strategy, only these situations can trigger change detection for the current component:
- The reference to the input property (binding) of the component was changed
- An asynchronous event is triggered inside the component
- Manually trigger change detection
- The current component or one of its child components fired an event, such as click
Briefly talk about the first point, this is a more classic processing method in angular. Others are more intuitive.
For example, the parent component passes an object to the child component using @Input
@Component({
template: `
<child [people]="people"></child>
`
})
export class AppComponent {
people = {
name: '张三'
};
onClick1() {
this.people.name = '李四';
}
onClick2() {
this.people = { name: '李四'};
}
}
Calling the onClick1 function of the parent component does not trigger change detection, because it only changes the object's properties, not the object's reference.
The onClick2 function will trigger change detection.
We can observe the behavior under the onPush strategy through the following figure.
When the default change detection is performed, the change detector does not update the subtree on the side of the onPush strategy. Using this behavior can improve performance by reducing unnecessary change detection when we have a good understanding of the component's change detection.
Summarize:
To automatically detect changes, Angular uses the ChangeDetectionStrategy.Default strategy by default, which ensures that our UI is displayed in a predictable and performant manner, suitable for most applications when changing no more than 50 components. For larger applications, Consider using the ChangeDetectionStrategy.OnPush strategy.
ChangeDetectorRef
Next, let's talk about manual change detection.
Manual change detection uses the ChangeDetectorRef
class provided by angular, which defines the following public interfaces.
class ChangeDetectorRef {
markForCheck() : void
detach() : void
reattach() : void
detectChanges() : void
checkNoChanges() : void
}
Suppose we have the following component tree
detach()
The first method that allows us to manipulate state is detach
which simply disables detection of the current view.
The method of use is also very simple.
export class AComponent {
constructor(public cd: ChangeDetectorRef) {
this.cd.detach();
}
This ensures that when running the following change detection, AppComponent will skip the left branch starting with (without checking components with a yellow background).
At the same time, if the state of AComponent has changed, its child components will not be checked.
reattach
Reattach the previously detached view to the change detection tree.
For example, we can use the reattach()
method to re-add the view disabled by detach() above.
E.g:
@Input()
set live(value: boolean) {
if (value) {
this.ref.reattach();
} else {
this.ref.detach();
}
}
markForCheck
This method applies when using the OnPush
strategy.
When a view changes detection strategy using OnPush (checkOnce), explicitly mark the view as dirty so that it can be checked again.
From what I've searched, it just iterates up and enables checks for each parent component up to the root.
detectChanges
Running a change detection on the current component and all its subcomponents is also the one we use most often.
checkNoChanges
This ensures that nothing has changed during the current change detection run. Throws an exception if a changed binding is found or if it is determined that the DOM should be updated.
Combine operation
For example, component data is expected to change continuously, many times per second. To improve performance, we want to check and update the list less frequently than changes actually occur. To do this, we can detach the component's change detector and perform a check every five seconds.
@Component({
selector: 'giant-list',
template: `
<li *ngFor="let d of dataProvider.data">Data {{d}}</li>
`,
})
class GiantList {
constructor(private ref: ChangeDetectorRef, public dataProvider: DataListProvider) {
ref.detach();
setInterval(() => {
this.ref.detectChanges();
}, 5000);
}
}
Summary: Understanding the ChangeDetectorRef class can help us understand the principle and behavior of change detection. When the default change detection does not satisfy our ideas, we can manually adjust the update of the view.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。