web-dev-qa-db-ja.com

Angular 2変更検出はどのように動作しますか?

Angular 1では、変更の検出は$ scope階層のダーティチェックによるものでした。テンプレート、コントローラー、またはコンポーネントにウォッチャーを暗黙的または明示的に作成します。

Angular 2には$ scopeがありませんが、setInterval、setTimeoutなどをオーバーライドします。 Angularがこれを使用して$ digestをトリガーする方法を見ることができますが、[Object.observeがブラウザーに入れなかったことを考えると、Angularはどのように変更を判断しますか?

以下に簡単な例を示します。サービスで定義されたオブジェクトは、setIntervalで更新されます。 DOMは各間隔で再コンパイルされます。

Angularは、AppComponentがサービスを監視していること、およびサービスの属性の値が変更されたことをどのように知ることができますか?

var InjectedService = function() {
  var val = {a:1}
  setInterval(() => val.a++, 1000);
  return val;
}

var AppComponent = ng.core
  .Component({
    selector: "app",
    template:
    `
      {{service.a}}
    `
  })
  .Class({
    constructor: function(service) {
      this.service = service;
    }
  })

AppComponent.parameters = [ new ng.core.Inject( InjectedService ) ];

document.addEventListener('DOMContentLoaded', function() {
  ng.platform.browser.bootstrap(AppComponent, [InjectedService])
});
30
superluminary

Angularは、コンポーネントごとに変更検出オブジェクト( ChangeDetectorRef を参照)を作成し、{{service.a}}などの各テンプレートバインディングの最後の値を追跡します。デフォルトでは、すべての非同期ブラウザイベント(サーバーからの応答、クリックイベント、タイムアウトイベントなど)の後、Angular変更検出が実行され、これらの変更検出を使用してすべてのバインディングがダーティチェックされますオブジェクト。

変更が検出された場合、変更は伝播されます。例えば。、

  • 入力プロパティの値が変更された場合、新しい値はコンポーネントの入力プロパティに伝播されます。
  • {{}}バインディング値が変更された場合、新しい値はDOMプロパティtextContentに伝播されます。
  • xの値がスタイル、属性、またはクラスバインディングで変更された場合、つまり[style.x]または[attr.x]または[class.x] –新しい値はDOMに伝播されますスタイル、HTML属性、またはクラスを更新します。

AngularはZone.jsを使用して独自のゾーン( NgZone )を作成します。これはすべての非同期イベント(ブラウザーDOMイベント、タイムアウト、AJAX/XHR)をパッチします。 。これが、各非同期イベント後に変更検出を自動的に実行できる方法です。つまり、各非同期イベントハンドラー(関数)が終了すると、Angular変更検出が実行されます。

この回答には詳細と参照リンクがあります: AngularJS $ watchと同等のAngular2とは何ですか?

35
Mark Rajcok

Zone.js

変更は何かに対するreactionとして発生するため、この点では非同期です。それらは非同期アクションによって引き起こされ、ブラウザの世界では Events 。これらのイベントをインターセプトするには、angularが zone.js を使用します。これはJavaScript呼び出しスタックにパッチを適用し(間違っている場合は誰かが修正してくれます)、他のアクションを実行するために使用されます。

_function angular() {...}
zone.run(angular);
_

このangular関数がAngular全体であると想像した場合、これはゾーンでの実行方法になります。そうすることでイベントをインターセプトすることができ、それらがトリガーされた場合、変更が発生したと想定し、それらをリッスン/ウォッチできます。

ApplicationRef

実際には ApplicationRef はゾーンを作成します:

_/**
 * Create an Angular zone.
 */
export function createNgZone(): NgZone {
  return new NgZone({enableLongStackTrace: assertionsEnabled()});
}
_

そして、クラスNgZoneいくつかのイベントエミッター で作成されます:

_  this._onTurnStartEvents = new EventEmitter(false);
  this._onTurnDoneEvents = new EventEmitter(false);
  this._onEventDoneEvents = new EventEmitter(false);
  this._onErrorEvents = new EventEmitter(false);
_

ゲッターを介して外部に公開すること:

_  get onTurnStart(): /* Subject */ any { return this._onTurnStartEvents; }
  get onTurnDone() { return this._onTurnDoneEvents; }
  get onEventDone() { return this._onEventDoneEvents; }
  get onError() { return this._onErrorEvents; }
_

ApplicationRef作成済み の場合、ゾーンのイベント、特にonTurnDone()にサブスクライブします。

_this.zone.onTurnDone
  .subscribe(() => this.zone.run(() => this.tick());
_

変更点

イベントがトリガーされると tick() すべてのコンポーネントをループする関数が実行されます:

_  this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
_

および 変更を検出 コンポーネントのChangeDetectionStrategyに基づきます。これらの変更は SimpleChange オブジェクトの配列として収集されます:

_addChange(changes: {[key: string]: any}, oldValue: any, newValue: any): {[key: string]: any} {
  if (isBlank(changes)) {
    changes = {};
  }
  changes[this._currentBinding().name] = ChangeDetectionUtil.simpleChange(oldValue, newValue);
  return changes;
}
_

魔女は onChanges インターフェイスを介して利用可能です:

_export interface OnChanges { 
  ngOnChanges(changes: {[key: string]: SimpleChange}); 
}
_
18
Sasxa