ChangeDetectionStrategy.OnPush
メカニズムを理解しようとしています。
読み取りから収集したのは、古い値を新しい値と比較することにより、変化の検出が機能するということです。オブジェクト参照が変更されていない場合、その比較はfalseを返します。
ただし、その「ルール」がバイパスされる特定のシナリオがあるようです。どのように機能するのか説明していただけますか?
さて、これは私の頭の中のすべてを解決するために履歴書を作成したことを理解するのに一晩かかったので、将来の読者に役立つかもしれません。それでは、いくつかのことを片付けることから始めましょう。
コンポーネントにはフィールドがあります。これらのフィールドは、ある種のイベントの後、そしてその後のみ変更されます。
マウスクリック、ajaxリクエスト、setTimeout ...としてイベントを定義できます。
角度データフローは一方通行です。つまり、データは子供から親に流れません。たとえば、_@Input
_タグを介した親から子へのみ。上位コンポーネントに子の変更を認識させる唯一の方法は、eventを使用することです。これにより、次のことが可能になります。
イベントが発生すると、angularフレームワークは、すべてのコンポーネントを上から下にチェックして、変更されているかどうかを確認します。変更があれば、それに応じてビューを更新します。
Angularは、イベントが発生した後にすべてのコンポーネントをチェックします。最下位レベルのコンポーネントであるコンポーネントでクリックイベントが発生したとします。つまり、コンポーネントには親はあるが子はありません。そのクリックは、イベントエミッタ、サービスなどを介して親コンポーネントの変更をトリガーする可能性があります。Angularは、親が変更されるかどうかわからない。そのためAngularは、デフォルトでイベントが発生した後にすべてのコンポーネントをチェックします。
それらが変更されたかどうかを確認するには、angularを使用してChangeDetector
クラスを使用します。
すべてのコンポーネントには、変更検出クラスが添付されています。何らかのイベント後にコンポーネントの状態が変化したかどうかを確認し、ビューを更新する必要があるかどうかを確認するために使用されます。イベントが発生すると(マウスクリックなど)、この変更検出プロセスはすべてのコンポーネントで(デフォルトで)発生します。
たとえば、ParentComponentがある場合:
_@Component({
selector: 'comp-parent',
template:'<comp-child [name]="name"></comp-child>'
})
class ParentComponent{
name:string;
}
_
次のようなParentComponent
に変更検出器を接続します。
_class ParentComponentChangeDetector{
oldName:string; // saves the old state of the component.
isChanged(newName){
if(this.oldName !== newName)
return true;
else
return false;
}
}
_
お気づきかもしれませんが、オブジェクトプロパティを変更すると、isChangedメソッドはfalseを返します。確かに
_let prop = {name:"cat"};
let oldProp = prop;
//change prop
prop.name = "dog";
oldProp === prop; //true
_
changeDetector
isChanged()
でtrueを返さずにオブジェクトプロパティを変更できる場合、angularは以下のすべてのコンポーネントも変更されたと想定します。すべてのコンポーネントの変更検出をチェックするだけです。
例:ここにはサブコンポーネントを持つコンポーネントがあります。変更検出は親コンポーネントに対してfalseを返しますが、子のビューは非常に適切に更新される必要があります。
_@Component({
selector: 'parent-comp',
template: `
<div class="orange" (click)="person.name='frank'">
<sub-comp [person]="person"></sub-comp>
</div>
`
})
export class ParentComponent {
person:Person = { name: "thierry" };
}
// sub component
@Component({
selector: 'sub-comp',
template: `
<div>
{{person.name}}
</div>
})
export class SubComponent{
@Input("person")
person:Person;
}
_
そのため、デフォルトの動作ではすべてのコンポーネントをチェックします。サブコンポーネントは入力が変更されていなければ変更できないにもかかわらず、angularは入力が変更されていないことを確実に知らない本当にが変更されたためです。それは同じかもしれませんが、異なるプロパティを持つことができます。
コンポーネントが_changeDetection: ChangeDetectionStrategy.OnPush
_でマークされている場合、angularは、オブジェクト参照が変更されなかった場合、入力オブジェクトが変更されなかったと想定します。したがって、ビューはモデルと同期しなくなります。
例
この例は、実際にこれを示しているため、素晴らしいものです。クリックすると、入力オブジェクト名のプロパティが変更される親コンポーネントがあります。親コンポーネント内のclick()
メソッドをチェックすると、コンソールで子コンポーネントプロパティが出力されることに気付くでしょう。そのプロパティは変更されました。しかし、視覚的に見ることはできません。これは、ビューが更新されていないためです。 OnPush戦略により、refオブジェクトが変更されなかったため、変更検出プロセスは発生しませんでした。
_@Component({
selector: 'my-app',
template: `
<div class="orange" (click)="click()">
<sub-comp [person]="person" #sub></sub-comp>
</div>
`
})
export class App {
person:Person = { name: "thierry" };
@ViewChild("sub") sub;
click(){
this.person.name = "Jean";
console.log(this.sub.person);
}
}
// sub component
@Component({
selector: 'sub-comp',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div>
{{person.name}}
</div>
`
})
export class SubComponent{
@Input("person")
person:Person;
}
export interface Person{
name:string,
}
_
クリック後、名前はビューではまだティエリーですが、コンポーネント自体ではありません
ここで、元の質問で私を混乱させたものに至ります。以下のコンポーネントはOnPush戦略でマークされていますが、ビューは変更されると更新されます。
_@Component({
selector: 'my-app',
template: `
<div class="orange" >
<sub-comp ></sub-comp>
</div>
`,
styles:[`
.orange{ background:orange; width:250px; height:250px;}
`]
})
export class App {
person:Person = { name: "thierry" };
click(){
this.person.name = "Jean";
console.log(this.sub.person);
}
}
// sub component
@Component({
selector: 'sub-comp',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="grey" (click)="click()">
{{person.name}}
</div>
`,
styles:[`
.grey{ background:#ccc; width:100px; height:100px;}
`]
})
export class SubComponent{
@Input()
person:Person = { name:"jhon" };
click(){
this.person.name = "mich";
}
}
_
そのため、ここでは、オブジェクト入力が参照を変更していないことがわかり、戦略OnPushを使用しています。そのため、更新されないと思われる可能性があります。実際には更新されます。
Gunterが答えで言ったように、それは、OnPush戦略では、次の場合にコンポーネントの変更検出が発生するためです。
戦略に関係なく。
_*ngFor
_は、独自の変更検出を行います。変更検出が実行されるたびに、NgFor
がngDoCheck()
メソッドを呼び出し、NgFor
が配列の内容が変更されたかどうかを確認します。
あなたの場合、コンストラクタはAngularがビューのレンダリングを開始する前に実行されるため、変更はありません。
たとえば、次のようなボタンを追加する場合
_<button (click)="persons.Push({name: 'dynamically added', id: persons.length})">add</button>
_
クリックすると、実際にngFor
が認識しなければならない変更が発生します。
_ChangeDetectionStrategy.OnPush
_を使用すると、OnPush
を使用すると変更検出が実行されるため、コンポーネントの変更検出が実行されます。
(click)
_@Input()
が変更検出によって更新されました| async
_パイプがイベントを受け取りましたApplication.tick
を防ぐには、changeDetectorをデタッチします。
constructor(private cd: ChangeDetectorRef) {
ngAfterViewInit() {
this.cd.detach();
}
In angular Parent-child structureを使用します。そこで、@ Inputsを使用してData form parentを子に渡します。
そこで、子の祖先で変更が発生すると、その祖先からのコンポーネントツリーで変更の検出が行われます。
しかし、ほとんどの状況では、入力が変更された場合にのみ、子のビューを更新する必要があります(Change Detectionを呼び出します)。これを実現するには、OnPushChangeDetectionStrategyを使用して、入力を変更します(使用不変)必要に応じて。 [〜#〜] link [〜#〜]