Angular で開発する場合、モデルからビューへの入力バインディング、ビューからモデルへの出力バインディング、ビューとモデル間の双方向バインディングなど、Angular でのバインディングをよく使用します。これらの境界値がビューとモデル間で同期できる理由は、Angular の変更検出によるものです。
簡単に言えば、変更検出は、ビューとモデルの間でバインドされた値が変更されたかどうかを検出するために Angular によって使用され、モデル内でバインドされた値が変更されたことが検出されると、ビューに同期されます。それ以外の場合、それが検出されたとき ビューにバインドされた値が変更されると、対応するバインディング関数がコールバックされます。
変更監視の鍵は、最小の粒度で境界値が変更されたかどうかを検出する方法です。では、どのような状況でこれらの境界値が変化するのでしょうか?私たちが使用するいくつかの一般的なシナリオを見てみましょう:
@Component({ selector: 'demo-component', template: ` <h1>{{name}}</h1> <button (click)="changeName()">change name</button> ` }) export class DemoComponent { name: string = 'Tom'; changeName() { this.name = 'Jerry'; } }
補間式を通じてテンプレート内の name 属性をバインドします。 名前変更ボタン
をクリックするとname属性の値が変更され、テンプレートビューの表示内容も変更されます。 change name按钮
时,改变了 name 属性的值,这时模板视图显示内容也发生了改变。
@Component({ selector: 'demo-component', template: ` <h1>{{name}}</h1> ` }) export class DemoComponent implements OnInit { name: string = 'Tom'; constructor(public http: HttpClient) {} ngOnInit() { // 假设有这个./getNewName请求,返回一个新值'Jerry' this.http.get('./getNewName').subscribe((data: string) => { this.name = data; }); } }
我们在这个组件的 ngOnInit 函数里向服务器端发送了一个 Ajax 请求,当这个请求返回结果时,同样会改变当前模板视图上绑定的 name 属性的值。
@Component({ selector: 'demo-component', template: ` <h1>{{name}}</h1> ` }) export class DemoComponent implements OnInit { name: string = 'Tom'; constructor() {} ngOnInit() { // 假设有这个./getNewName请求,返回一个新值'Jerry' setTimeout(() => { this.name = 'Jerry'; }, 1000); } }
我们在这个组件的ngOnInit函数里通过设定一个定时任务,当定时任务执行时,同样会改变当前视图上绑定的name属性的值。
其实,我们不难发现上述三种情况都有一个共同点,即这些导致绑定值发生改变的事件都是异步发生的。
Angular并不是捕捉对象的变动,它采用的是在适当的时机去检验对象的值是否被改动,这个时机就是这些异步事件的发生。
这个时机是由 NgZone 这个服务去掌控的,它获取到了整个应用的执行上下文,能够对相关的异步事件发生、完成或者异常等进行捕获,然后驱动 Angular 的变化监测机制执行。
通过上面的介绍,我们大致明白了变化检测是如何被触发的,那么 Angular 中的变化监测是如何执行的呢?
首先我们需要知道的是,对于每一个组件,都有一个对应的变化监测器;即每一个 Component 都对应有一个changeDetector
,我们可以在 Component 中通过依赖注入来获取到changeDetector
。
而我们的多个 Component 是一个树状结构的组织,由于一个 Component 对应一个changeDetector
,那么changeDetector
@Component({ selector: 'demo-child', template: ` <h1>{{title}}</h1> <p>{{paramOne}}</p> <p>{{paramTwo}}</p> ` }) export class DemoChildComponent { title: string = '子组件标题'; @Input() paramOne: any; // 输入属性1 @Input() paramTwo: any; // 输入属性2 }
@Component({ selector: 'demo-parent', template: ` <h1>{{title}}</h1> <demo-child [paramOne]='paramOneVal' [paramTwo]='paramTwoVal'></demo-child> <button (click)="changeVal()">change name</button> ` }) export class DemoParentComponent { title: string = '父组件标题'; paramOneVal: any = '传递给paramOne的数据'; paramTwoVal: any = '传递给paramTwo的数据'; changeVal() { this.paramOneVal = '改变之后的传递给paramOne的数据'; } }
実際、上記の 3 つの状況には 1 つの共通点があることを見つけるのは難しくありません。つまり、バインディング値の変更を引き起こすこれらのイベントはすべて非同期で発生します。
Angular はオブジェクトの変更をキャプチャしません。このタイミングは、オブジェクトの値が変更されたかどうかを確認するために使用されます。このタイミングは、これらの非同期イベントの発生です。changeDetector
に対応しており、コンポーネント内の依存関係注入を通じて取得できます。 changeDetector
に変更します。 changeDetector
に対応するため、changeDetector
もツリー構造で編成されています。 最後に覚えておく必要があるのは、すべての変更の監視はコンポーネント ツリーのルートから始まるということです。 @Component({ selector: 'demo-parent', template: ` <h1>{{title}}</h1> ` }) export class DemoParentComponent implements OnInit { title: string = '组件标题'; constructor(public cdRef: ChangeDetectorRef) {} ngOnInit() { this.cdRef.detach(); // 停止组件的变化监测,看需求使用不同的方法 } }
rrreee
上記のコードでは、DemoParentComponent は、ツリー構造の観点から、DemoParentComponent のボタンをクリックすると、changeVal メソッドが呼び戻され、変更監視の実行がトリガーされます。変更監視プロセスは次のとおりです。
OnPush 与 Default 之间的差别:当检测到与子组件输入绑定的值没有发生改变时,变化检测就不会深入到子组件中去。
上面说到我们可以修改组件元数据属性 changeDetection 来修改组件的变化监测策略(ChangeDetectionStrategy.Default 或 ChangeDetectionStrategy.OnPush),除了这个,我们还可以使用 ChangeDetectorRef 来更加灵活的控制组件的变化监测。
Angular 在整个运行期间都会为每一个组件创建 ChangeDetectorRef 的实例,该实例提供了相关方法来手动管理变化监测。有了这个类,我们自己就可以自定义组件的变化监测策略了,如停止/启用变化监测或者按指定路径变化监测等等。
相关方法如下:
markForCheck():把根组件到该组件之间的这条路径标记起来,通知Angular在下次触发变化监测时必须检查这条路径上的组件。
detach():从变化监测树中分离变化监测器,该组件的变化监测器将不再执行变化监测,除非再次手动执行reattach()方法。
reattach():把分离的变化监测器重新安装上,使得该组件及其子组件都能执行变化监测。
detectChanges():手动触发执行该组件到各个子组件的一次变化监测。
使用方法也很简单,直接在组件中注入即可:
@Component({ selector: 'demo-parent', template: ` <h1>{{title}}</h1> ` }) export class DemoParentComponent implements OnInit { title: string = '组件标题'; constructor(public cdRef: ChangeDetectorRef) {} ngOnInit() { this.cdRef.detach(); // 停止组件的变化监测,看需求使用不同的方法 } }
相关推荐:
以上がAngular 開発の実践 (5): 変更監視の詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。