ホームページ  >  記事  >  ウェブフロントエンド  >  Angular 開発の実践 (5): 変更監視の詳細な分析

Angular 開発の実践 (5): 変更監視の詳細な分析

不言
不言オリジナル
2018-04-02 15:03:181227ブラウズ

変更モニタリングとは

Angular で開発する場合、モデルからビューへの入力バインディング、ビューからモデルへの出力バインディング、ビューとモデル間の双方向バインディングなど、Angular でのバインディングをよく使用します。これらの境界値がビューとモデル間で同期できる理由は、Angular の変更検出によるものです。

簡単に言えば、変更検出は、ビューとモデルの間でバインドされた値が変更されたかどうかを検出するために Angular によって使用され、モデル内でバインドされた値が変更されたことが検出されると、ビューに同期されます。それ以外の場合、それが検出されたとき ビューにバインドされた値が変更されると、対応するバインディング関数がコールバックされます。

変更監視のソース

変更監視の鍵は、最小の粒度で境界値が変更されたかどうかを検出する方法です。では、どのような状況でこれらの境界値が変化するのでしょうか?私たちが使用するいくつかの一般的なシナリオを見てみましょう:

イベント: click/hover/...

@Component({
  selector: 'demo-component',
  template: `
    <h1>{{name}}</h1>
    <button (click)="changeName()">change name</button>
  `
})
export class DemoComponent {
    name: string = &#39;Tom&#39;;
    
    changeName() {
        this.name = &#39;Jerry&#39;;
    }
}

補間式を通じてテンプレート内の name 属性をバインドします。 名前変更ボタンをクリックするとname属性の値が変更され、テンプレートビューの表示内容も変更されます。 change name按钮时,改变了 name 属性的值,这时模板视图显示内容也发生了改变。

XHR/webSocket

@Component({
  selector: &#39;demo-component&#39;,
  template: `
    <h1>{{name}}</h1>
  `
})
export class DemoComponent implements OnInit {
    name: string = &#39;Tom&#39;;
    
    constructor(public http: HttpClient) {}
    
    ngOnInit() {
        // 假设有这个./getNewName请求,返回一个新值&#39;Jerry&#39;
        this.http.get(&#39;./getNewName&#39;).subscribe((data: string) => {
            this.name = data;
        });
    }
}

我们在这个组件的 ngOnInit 函数里向服务器端发送了一个 Ajax 请求,当这个请求返回结果时,同样会改变当前模板视图上绑定的 name 属性的值。

Times: setTimeout/requestAnimationFrame

@Component({
  selector: &#39;demo-component&#39;,
  template: `
    <h1>{{name}}</h1>
  `
})
export class DemoComponent implements OnInit {
    name: string = &#39;Tom&#39;;
    
    constructor() {}
    
    ngOnInit() {
        // 假设有这个./getNewName请求,返回一个新值&#39;Jerry&#39;
        setTimeout(() => {
            this.name = &#39;Jerry&#39;;
        }, 1000);
    }
}

我们在这个组件的ngOnInit函数里通过设定一个定时任务,当定时任务执行时,同样会改变当前视图上绑定的name属性的值。

总结

  • 其实,我们不难发现上述三种情况都有一个共同点,即这些导致绑定值发生改变的事件都是异步发生的。

  • Angular并不是捕捉对象的变动,它采用的是在适当的时机去检验对象的值是否被改动,这个时机就是这些异步事件的发生。

  • 这个时机是由 NgZone 这个服务去掌控的,它获取到了整个应用的执行上下文,能够对相关的异步事件发生、完成或者异常等进行捕获,然后驱动 Angular 的变化监测机制执行。

变化监测的处理机制

通过上面的介绍,我们大致明白了变化检测是如何被触发的,那么 Angular 中的变化监测是如何执行的呢?

首先我们需要知道的是,对于每一个组件,都有一个对应的变化监测器;即每一个 Component 都对应有一个changeDetector,我们可以在 Component 中通过依赖注入来获取到changeDetector

而我们的多个 Component 是一个树状结构的组织,由于一个 Component 对应一个changeDetector,那么changeDetector

XHR/webSocket

@Component({
  selector: &#39;demo-child&#39;,
  template: `
    <h1>{{title}}</h1>
    <p>{{paramOne}}</p>
    <p>{{paramTwo}}</p>
  `
})
export class DemoChildComponent {
    title: string = &#39;子组件标题&#39;;
    @Input() paramOne: any; // 输入属性1
    @Input() paramTwo: any; // 输入属性2
}

このコンポーネントの ngOnInit 関数でサーバーに Ajax リクエストを送信します。このリクエストが結果を返すと、現在のテンプレート ビューにバインドされている name 属性の値も変更されます。

Times: setTimeout/requestAnimationFrame

@Component({
  selector: &#39;demo-parent&#39;,
  template: `
    <h1>{{title}}</h1>
    <demo-child [paramOne]=&#39;paramOneVal&#39; [paramTwo]=&#39;paramTwoVal&#39;></demo-child>
    <button (click)="changeVal()">change name</button>
  `
})
export class DemoParentComponent {
    title: string = &#39;父组件标题&#39;;
    paramOneVal: any = &#39;传递给paramOne的数据&#39;;
    paramTwoVal: any = &#39;传递给paramTwo的数据&#39;;
    
    changeVal() {
        this.paramOneVal = &#39;改变之后的传递给paramOne的数据&#39;;
    }
}

このコンポーネントの ngOnInit 関数でスケジュールされたタスクを設定します。スケジュールされたタスクが実行されると、現在のビューにバインドされている name 属性の値も変更されます。

概要

実際、上記の 3 つの状況には 1 つの共通点があることを見つけるのは難しくありません。つまり、バインディング値の変更を引き起こすこれらのイベントはすべて非同期で発生します。

Angular はオブジェクトの変更をキャプチャしません。このタイミングは、オブジェクトの値が変更されたかどうかを確認するために使用されます。このタイミングは、これらの非同期イベントの発生です。
  • このタイミングは NgZone サービスによって制御され、アプリケーション全体の実行コンテキストを取得し、関連する非同期イベントの発生、完了、例外をキャプチャして、Angular の変更監視メカニズムを実行します。
  • 変更監視の処理メカニズム
  • 上記の紹介を通じて、変更検出がどのようにトリガーされるかがおおよそ理解できました。それでは、Angular では変更監視がどのように実行されるのでしょうか?

    まず知っておく必要があるのは、各コンポーネントに対応する変更検出器があるということです。つまり、各コンポーネントは changeDetector に対応しており、コンポーネント内の依存関係注入を通じて取得できます。 changeDetector に変更します。
複数のコンポーネントはツリー構造で編成されています。1 つのコンポーネントが changeDetector に対応するため、changeDetector もツリー構造で編成されています。

最後に覚えておく必要があるのは、すべての変更の監視はコンポーネント ツリーのルートから始まるということです。
  • 例:

    子コンポーネント:
  • @Component({
      selector: &#39;demo-parent&#39;,
      template: `
        <h1>{{title}}</h1>
      `
    })
    export class DemoParentComponent implements OnInit {
        title: string = &#39;组件标题&#39;;
        
        constructor(public cdRef: ChangeDetectorRef) {}
        
        ngOnInit() {
            this.cdRef.detach(); // 停止组件的变化监测,看需求使用不同的方法
        }
    }
  • 親コンポーネント:

    rrreee

    上記のコードでは、DemoParentComponent は、ツリー構造の観点から、5354ebd8efe1b41f1d27b438d97f8d03733dbcdb0cb52d6c005bb34feed27005 タグを通じて DemoChildComponent を埋め込みます。は DemoChildComponent のルート ノードであり、DemoChildComponent は DemoParentComponent のリーフ ノードです。
  • DemoParentComponent のボタンをクリックすると、changeVal メソッドが呼び戻され、変更監視の実行がトリガーされます。変更監視プロセスは次のとおりです。

まず、DemoParentComponent から変更検出が開始されます。 :

タイトルの値が発生したかどうかを検出します。 変更: 変化なし

paramOneValの値が変更されたかどうかを検出: 変化しました(ボタンをクリックしてchangeVal()メソッドを呼び出すことで変更されます)🎜🎜🎜🎜かどうかを検出しますparamTwoVal の値が変更されました: 変更なし🎜🎜🎜🎜 次に、変更検出がリーフ ノード DemoChildComponent に移動します: 🎜🎜🎜🎜タイトルの値が変更されたかどうかを検出します: 変更なし🎜🎜🎜🎜 paramOne が変更されたかどうかを検出します: 変更されました (期限付き)親コンポーネントの属性の変更 paramOneVal)🎜 🎜🎜🎜 paramTwo が変更されたかどうかを検出します: 変更なし🎜🎜🎜🎜 最後に、DemoChildComponent にはリーフ ノードがなくなったため、変更監視によって DOM が更新され、ビューとビューの間で変更が同期されます。モデル。 🎜🎜変更監視戦略🎜🎜変更監視の処理メカニズムを学習した後、このメカニズムは少し単純すぎて粗雑だと思うかもしれません。アプリケーションに数百または数千のコンポーネントがあり、いずれかのコンポーネントが監視をトリガーすると仮定します。ルートノードからリーフノードまで再検出する必要があります。 🎜🎜心配しないでください。Angular の開発チームはすでにこの問題を検討しています。上記の検出メカニズムは単なるデフォルトの検出メカニズムです (メタデータ属性のchangeDetection: ChangeDetectionStrategy.OnPushを設定します)。 🎜

OnPush 与 Default 之间的差别:当检测到与子组件输入绑定的值没有发生改变时,变化检测就不会深入到子组件中去

变化监测类 - ChangeDetectorRef

上面说到我们可以修改组件元数据属性 changeDetection 来修改组件的变化监测策略(ChangeDetectionStrategy.Default 或 ChangeDetectionStrategy.OnPush),除了这个,我们还可以使用 ChangeDetectorRef 来更加灵活的控制组件的变化监测。

Angular 在整个运行期间都会为每一个组件创建 ChangeDetectorRef 的实例,该实例提供了相关方法来手动管理变化监测。有了这个类,我们自己就可以自定义组件的变化监测策略了,如停止/启用变化监测或者按指定路径变化监测等等。

相关方法如下:

  • markForCheck():把根组件到该组件之间的这条路径标记起来,通知Angular在下次触发变化监测时必须检查这条路径上的组件。

  • detach():从变化监测树中分离变化监测器,该组件的变化监测器将不再执行变化监测,除非再次手动执行reattach()方法。

  • reattach():把分离的变化监测器重新安装上,使得该组件及其子组件都能执行变化监测。

  • detectChanges():手动触发执行该组件到各个子组件的一次变化监测。

使用方法也很简单,直接在组件中注入即可:

@Component({
  selector: &#39;demo-parent&#39;,
  template: `
    <h1>{{title}}</h1>
  `
})
export class DemoParentComponent implements OnInit {
    title: string = &#39;组件标题&#39;;
    
    constructor(public cdRef: ChangeDetectorRef) {}
    
    ngOnInit() {
        this.cdRef.detach(); // 停止组件的变化监测,看需求使用不同的方法
    }
}

相关推荐:

Angular开发实践(四):组件之间的交互

Angular开发实践(二):HRM运行机制

         

以上がAngular 開発の実践 (5): 変更監視の詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。