違いは何であるChangeDetectorRef.markForCheck()
とはChangeDetectorRef.detectChanges()
?
私はSOに関する情報のみを見つけましたNgZone.run()
が、これら2つの関数の違いはわかりません。
ドキュメントへの参照のみの回答の場合は、どちらかを選択する実際的なシナリオをいくつか示してください。
違いは何であるChangeDetectorRef.markForCheck()
とはChangeDetectorRef.detectChanges()
?
私はSOに関する情報のみを見つけましたNgZone.run()
が、これら2つの関数の違いはわかりません。
ドキュメントへの参照のみの回答の場合は、どちらかを選択する実際的なシナリオをいくつか示してください。
回答:
ドキュメントから:
detectChanges():void
つまり、モデル(クラス)内の何かが変更されたが、ビューが反映されていない場合は、Angularにそれらの変更を検出(ローカルの変更を検出)してビューを更新するよう通知する必要があるかもしれません。
可能なシナリオは次のとおりです。
1-変更検出器がビューから切り離されます(detachを参照)
2-更新が行われましたが、Angularゾーン内にないため、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など)によって引き起こされたモデルの変更を確認し、変更検出を実行してビューを更新しましたが、それを終えましたが、別の方法がありましたモデルを再度更新したコード内の関数。AngularJSのようなダーティーチェックがなくなったので、変更検出を再度実行したくありません。Dでは、一方向のデータフローを使用する必要があります。
あなたは間違いなくこのエラーに出くわすでしょう:P。
それを修正する方法のカップル:
1- 適切な方法:更新が変更検出サイクル内にあることを確認します(Angular2の更新は一度だけ発生する一方向のフローであり、その後モデルを更新せず、コードをより適切な場所/時間に移動します)。
2- 遅延方法:更新後にdetectChanges()を実行してangular2を幸せにします。これは間違いなく最良の方法ではありませんが、考えられるシナリオを尋ねたところ、これはその1つです。
このようにあなたは言っています:変更検出を実行したことを心から知っていますが、チェックを完了した後にオンザフライで何かを更新しなければならなかったので、もう一度実行して欲しいのです。
3- ゾーンごとにパッチが適用され、終了後に実行されるsetTimeout
ため、コードを内に配置します。setTimeout
detectChanges
ドキュメントから
markForCheck() : void
ときこれがほとんど必要とされている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()
角度がそれの変化検出を実行した後、またはアップデートは、すべての角度の世界にされていない場合は、モデルを更新しました。
使用markForCheck()
あなたはOnPushを使用していて、迂回している場合ChangeDetectionStrategy
、一部のデータを変異したり、内部のモデルを更新したことでのsetTimeout。
detectChanges
ビューを更新します。この詳細な説明を参照してください。
this.cdMode === ChangeDetectorStatus.Checked
は、ビューが更新されない場合を意味しています。そのため、markForCheckを使用します。
detectChanges
。そしてcdMode
Angular にはありません4.x.x
。それについては私の記事に書いています。よかったです。メディアでおすすめするか、フォローしてください:)
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は、変更検出を手動でトリガーする必要がある可能性がある理由を説明しました。
先ほど述べたように、この人は変更検出をまったくトリガーしません。現在のコンポーネントからルートコンポーネントに上向きに移動し、ビューステートをに更新します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での変更検出について知っておくべきこともすべて参照してください。
markForCheck
です。したがって、非同期パイプを使用していない場合は、おそらくそれを使用する必要があります。ただし、変更の検出を開始するための非同期イベントの結果としてストアの更新が発生することに注意してください。それはほとんどの場合です。しかし、例外もあるblog.angularindepth.com/...
async pipe
我々は通常のように行うには、いくつかのものを持っているサブスクライブの内側にあるためcall setFromValues
do some comparison
...とあればasync
、それ自体が呼び出してmarkForCheck
、我々は自分自身それを呼び出す場合いただきました!問題は?しかし、繰り返しになりますが、通常ngOnInit
、異なるデータを取得するセレクターは2〜3個、場合によってはそれ以上markForCheck
あり、それらすべてを呼び出します。
cd.detectChanges()
現在のコンポーネントからその子孫まで、すぐに変更検出を実行します。
cd.markForCheck()
変更検出を実行しませんが、祖先を変更検出を実行する必要があるとしてマークします。次に変更検出がどこでも実行されると、マークされたコンポーネントに対しても実行されます。
cd.markForCheck()
。多くの場合、変更は複数のコンポーネントに影響し、どこかで変更検出が呼び出されます。あなたは本質的に言っています:それが起こったときにこのコンポーネントも更新されることを確認しましょう。(ビューは、私が書いたすべてのプロジェクトですぐに更新されますが、すべての単体テストでは更新されません)。cd.detectChanges()
は、を使用してください。その場合はエラーになります。これはおそらく、Angularの変更検出が設計されているという前提に反して動作している祖先コンポーネントの状態を編集しようとしていることを意味します。 cd.markForCheck()
detectChanges()
detectChanges()
。markForCheck()
実際に時間内にビューを更新しない場合があります。何かを単体テストすると、ビューに影響します。たとえば、fixture.detectChanges()
アプリ自体では不要な場合、手動で呼び出す必要があります。detectChanges()
、コンポーネントの祖先に対して不必要に変更検出を実行していないため、を使用するとパフォーマンスが向上する可能性があります。