238

ChangeDetectorRef.markForCheck()とはどう違いChangeDetectorRef.detectChanges()ますか?

SOの違いに関する情報のみを見つけましNgZone.run()たが、これら 2 つの関数の違いはありません。

ドキュメントへの参照のみを含む回答については、いくつかの実際的なシナリオを示して、どちらかを選択してください。

4

4 に答える 4

310

detectChanges() : ボイド

変更ディテクタとその子をチェックします。

つまり、モデル (クラス) 内の何かが変更されたがビューが反映されていない場合、それらの変更を検出 (ローカルの変更を検出) してビューを更新するように Angular に通知する必要がある場合があります。

考えられるシナリオは次のとおりです。

1- 変更検出器がビューから切り離されている ( detachを参照)

2- 更新が発生しましたが、Angular Zone 内にないため、Angular はそれを認識していません。

サードパーティ関数がモデルを更新し、その後ビューを更新したい場合などです。

 someFunctionThatIsRunByAThirdPartyCode(){
     yourModel.text = "new text";
 }

このコードは (おそらく) Angular のゾーンの外にあるため、変更を検出してビューを更新する必要がある可能性が最も高いです。

 myFunction(){
   someFunctionThatIsRunByAThirdPartyCode();

   // Let's detect the changes that above function made to the model which Angular is not aware of.
    this.cd.detectChanges();
 }

:

上記の作業を行う方法は他にもあります。つまり、その変更を Angular の変更サイクル内に持ち込む方法は他にもあります。

** そのサードパーティ関数を zone.run 内にラップできます:

 myFunction(){
   this.zone.run(this.someFunctionThatIsRunByAThirdPartyCode);
 }

** 関数を setTimeout 内にラップできます:

myFunction(){
   setTimeout(this.someFunctionThatIsRunByAThirdPartyCode,0);
 }

3-change detection cycle終了後にモデルを更新する場合もあります。その場合、次の恐ろしいエラーが発生します。

「確認後に式が変更されました」;

これは一般的に (Angular2 言語から) を意味します。

受け入れられた方法 (イベント、XHR リクエスト、setTimeout など) の 1 つによって引き起こされたモデルの変更を確認し、変更検出を実行してビューを更新し、それを終了しましたが、別の方法がありました。モデルを再度更新したコード内の関数であり、AngularJS のようなダーティ チェックがもうないため、変更検出を再度実行したくありません :D そして、一方向のデータ フローを使用する必要があります!

あなたは間違いなくこのエラーに遭遇するでしょう:P .

それを修正するいくつかの方法:

1-適切な方法: 更新が変更検出サイクル内にあることを確認します (Angular2 の更新は、一度発生する一方向のフローです。その後はモデルを更新せず、コードをより適切な場所/時間に移動します)。

2-怠惰な方法: angular2 を満足させるために更新後に detectChanges() を実行します。

このようにあなたは言っています:私はあなたが変更検出を実行したことを心から知っていますが、あなたがチェックを終えた後にその場で何かを更新しなければならなかったので、もう一度やり直してほしい.

3-ゾーンによってパッチが適用され、終了後に実行されるsetTimeoutため、コードを の中に入れます。setTimeoutdetectChanges


ドキュメントから

markForCheck() : void

すべての ChangeDetectionStrategy の祖先をチェック対象としてマークします。

これは主に、コンポーネントのChangeDetectionStrategyがOnPushの場合に必要です。

OnPush 自体は、次のいずれかが発生した場合にのみ変更検出を実行することを意味します。

1- @Input プロパティの参照が完全に変更された場合、コンポーネントの @inputs の 1 つが新しい値に完全に置き換えられました。

したがって、コンポーネントの ChangeDetectionStrategy が OnPush の場合、次のようになります。

   var obj = {
     name:'Milad'
   };

そして、次のように更新/変更します:

  obj.name = "a new name";

これはobj参照を更新しないため、変更検出は実行されないため、ビューは更新/突然変異を反映していません。

この場合、Angular にビューをチェックして更新するように手動で指示する必要があります (markForCheck)。

したがって、これを行った場合:

  obj.name = "a new name";

これを行う必要があります:

  this.cd.markForCheck();

むしろ、以下では変更検出が実行されます。

    obj = {
      name:"a new name"
    };

以前の obj を新しい{};に完全に置き換えました。

2- クリックなどのイベントが発生したか、子コンポーネントのいずれかがイベントを発行しました。

次のようなイベント:

  • クリック
  • キーアップ
  • サブスクリプション イベント

要するに:

  • detectChanges()angularが変更検出を実行した後にモデルを更新した場合、または更新が角度の世界にまったくない場合に使用します。

  • markForCheck()OnPush を使用していて、一部のデータを変更してバイパスしている場合、またはsetTimeoutChangeDetectionStrategy内でモデルを更新した場合に使用します。

于 2016-12-28T14:54:50.977 に答える
134

2 つの最大の違いは、detectChanges()実際には変更検出をトリガーするが、変更検出をmarkForCheck()トリガーしないことです。

検出変更

これは、トリガーするコンポーネントから始まるコンポーネントのツリーの変更検出を実行するために使用されますdetectChanges()。そのため、現在のコンポーネントとそのすべての子に対して変更検出が実行されます。Angular はルート コンポーネント ツリーへの参照を保持し、ApplicationRef非同期操作が発生すると、ラッパー メソッドを通じてこのルート コンポーネントの変更検出をトリガーしますtick()

@Injectable()
export class ApplicationRef_ extends ApplicationRef {
  ...
  tick(): void {
    if (this._runningTick) {
      throw new Error('ApplicationRef.tick is called recursively');
    }

    const scope = ApplicationRef_._tickScope();
    try {
      this._runningTick = true;
      this._views.forEach((view) => view.detectChanges()); <------------------

viewこれがルート コンポーネント ビューです。「複数のコンポーネントをブートストラップするとどうなるか」で説明したように、多くのルート コンポーネントが存在する可能性があります。

@milad は、変更検出を手動でトリガーする必要がある可能性がある理由を説明しました。

markForCheck

私が言ったように、この男は変更検出をまったくトリガーしません。現在のコンポーネントからルート コンポーネントまで単純に上に移動し、それらのビュー ステートを に更新しますChecksEnabled。ソースコードは次のとおりです。

export function markParentViewsForCheck(view: ViewData) {
  let currView: ViewData|null = view;
  while (currView) {
    if (currView.def.flags & ViewFlags.OnPush) {
      currView.state |= ViewState.ChecksEnabled;  <-----------------
    }
    currView = currView.viewContainerParent || currView.parent;
  }
}

コンポーネントの実際の変更検出はスケジュールされていませんが、将来 (現在または次の CD サイクルの一部として) 変更が検出されると、変更検出器が切り離されていたとしても、親コンポーネント ビューがチェックされます。変更検出戦略を使用するcd.detach()か指定することにより、変更検出機能を切り離すことができます。OnPushすべてのネイティブ イベント ハンドラーは、すべての親コンポーネント ビューをチェック対象としてマークします。

このアプローチは、ngDoCheckライフサイクル フックでよく使用されます。詳細については、コンポーネントがチェックされていると思われる場合ngDoCheck— この記事を読んでください.

詳細については、Angular での変更検出について知っておく必要があるすべても参照してください。

于 2017-07-30T05:40:51.767 に答える