我们知道,如果我们绑定了组件数据到视图,例如使用 <p>{{content}}</p>,如果我们在组件中改变了content的值,那么视图也会更新为对应的值。
angular 会在我们的组件发生变化的时候,对我们的组件执行变化检测,如果检测到我们的数据发生了变化,就会执行某些操作,如修改绑定数据的时候更新视图。
这样一来,当我们的组件数据比较多的时候,angular就会有很多操作在静悄悄地进行,一个规避这个问题的方法是,设置某个组件的变化检测策略为 ‘OnPush‘。
使用 OnPush 会指引 angular 去检测那些引用发生变化的对象,而不是某个对象下的属性发生变化就执行变化检测的操作。
该方法有以下几个方法:
class ChangeDetectorRef {
markForCheck(): void
detach(): void
detectChanges(): void
checkNoChanges(): void
reattach(): void
}
class ChangeDetectorRef { markForCheck(): void detach(): void detectChanges(): void checkNoChanges(): void reattach(): void }
detach 和 reattach
先来说 detach 和 reattach,顾名思义,顾名思义... 只可意会
detach 方法的用处是,告诉 angular,暂时不用管当前组件的变化了,也就是说,当我们在组件上调用 ChangeDetectorRef 的 detach 方法之后,angular 在该组件数据发生变化的时候,不再执行更新视图等操作。
相反的,reattach 就是让 angular 在组件数据发生变化的时候重新检测该组件的变化,在该组件数据变化的时候,执行如更新视图的操作。
import {ChangeDetectorRef, Component} from ‘@angular/core‘;
class DataProvider {
data = 1;
constructor() {
setInterval(() => {
this.data = this.data * 2;
}, 500);
}
}
@Component({
selector: ‘live-data‘,
inputs: [‘live‘],
template: ‘Data: {{dataProvider.data}}‘
})
export class LiveData {
constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {}
set live(value) {
if (value) {
this.ref.reattach();
} else {
this.ref.detach();
}
}
}
@Component({
selector: ‘app-root‘,
providers: [DataProvider],
template: `
Live Update: <input type="checkbox" [(ngModel)]="live">
<live-data [live]="live"></live-data>
`,
})
export class AppComponent {
live = true;
}
import {ChangeDetectorRef, Component} from ‘@angular/core‘; class DataProvider { data = 1; constructor() { setInterval(() => { this.data = this.data * 2; }, 500); } } @Component({ selector: ‘live-data‘, inputs: [‘live‘], template: ‘Data: {{dataProvider.data}}‘ }) export class LiveData { constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {} set live(value) { if (value) { this.ref.reattach(); } else { this.ref.detach(); } } } @Component({ selector: ‘app-root‘, providers: [DataProvider], template: ` Live Update: <input type="checkbox" [(ngModel)]="live"> <live-data [live]="live"></live-data> `, }) export class AppComponent { live = true; }
上面的例子中,当我们勾中 checkbox 的时候,会发现,右边的数字一直在更新,而当我们取消勾选的时候,数字停止了更新。
也就是说,当我们 detach 的时候,angular 从变化检测树上移除了该组件,reattach 的时候,angular 把该组件重新加到了变化检测树上。
markForCheck
(调用 markForCheck 方法后,(即使没有输入,也没有事件发射的情况下)会进行脏值检测)
(method) ChangeDetectorRef.markForCheck(): void
When a view uses the {@link ChangeDetectionStrategy#OnPush OnPush} (checkOnce) change detection strategy, explicitly marks the view as changed so that it can be checked again.
Components are normally marked as dirty (in need of rerendering) when inputs have changed or events have fired in the view. Call this method to ensure that a component is checked even if these triggers have not occured.
<!-- TODO: Add a link to a chapter on OnPush components -->
说到这个就不得不说一说 angular 的 ChangeDetectionStrategy 了,参见另外一篇文章:todo
当我们调用 ChangeDetectorRef 的 markForCheck 方法之后,angular 会在变化检测周期中检测该组件,
如果我们设置了组件的 changeDetection 为 OnPush 的时候,不使用 markForCheck 方法我们更新数据视图是不会更新的。
@Component({
selector: ‘cmp‘,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `Number of ticks: {{numberOfTicks}}`
})
export class Cmp {
numberOfTicks = 0;
constructor(private ref: ChangeDetectorRef) {
setInterval(() => {
this.numberOfTicks++;
// 如果我们不调用下面这一句,视图将不会更新
this.ref.markForCheck();
}, 1000);
}
}
@Component({
selector: ‘app-root‘,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<cmp></cmp>`
})
export class App {}
@Component({ selector: ‘cmp‘, changeDetection: ChangeDetectionStrategy.OnPush, template: `Number of ticks: {{numberOfTicks}}` }) export class Cmp { numberOfTicks = 0; constructor(private ref: ChangeDetectorRef) { setInterval(() => { this.numberOfTicks++; // 如果我们不调用下面这一句,视图将不会更新 this.ref.markForCheck(); }, 1000); } } @Component({ selector: ‘app-root‘, changeDetection: ChangeDetectionStrategy.OnPush, template: `<cmp></cmp>` }) export class App {}
detectChanges
检查一遍 变化检测器 (change detector) 和它的子检测器。
可以配合 ChangeDetectorRef 来实现局部变化检查。
有些時候,对于更新频繁数据量又很大的列表,我们不想 angular 在列表变化的时候马上更新视图,我们可以手动去调用 detectChanges 方法,去强制 angular 检查 组件数据的变化。
这样一来,就可以实现自主控制视图更新频率,而不用担心有太大的性能问题了。
checkNoChanges
检测该组件及其子组件,如果有变化存在则报错,用于开发阶段二次验证变化已经完成。