0

パフォーマンス上の理由から、コンポーネントに手動の変更検出を設定しようとしています。

アプリの構造: アプリ -> 本 -> ページ

AppComponent でオブザーバブルをサブスクライブし、ChangeDetectorRef の「markForCheck」メソッドを実行します。

これにより、BookComponent (更新用にマークしたコンポーネントの子) で ngAfterContentChecked メソッドがトリガーされるようです。

これにより、親コンポーネントの「markForCheck」もすべての子を更新すると信じていました。

しかし、ngAfterContentChecked が子 (BookComponent) で実行されても、このコンポーネントのテンプレートは更新されません!

すべての子 (コンポーネント階層の数レベルの深さの場合もあります) で観察対象をリッスンすることになっていますか? それとも私は何か間違ったことをしましたか?また、テンプレートが更新されないのに、なぜ ngAfterContentChecked が子コンポーネントで実行されるのでしょうか?

関連するコードの一部。

@Component({
    selector: 'app',
    directives: [BookComponent],
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: `
        <book *ngIf="_book" [book]="_book" [style.height]="_availableDimensions.height+'px'" (window:resize)="onResize($event)"></book>
        <footer id="footer"></footer>
    `
})

export class PlayerComponent {
    ngOnInit() {
        this.initScale();
    }

initScale(){
        this._playerService.availableDimensions$.subscribe(
            availableDimensions => {
                // set local availableDimensions variable
                this._availableDimensions = availableDimensions;
                this._cd.markForCheck();
            }
        );
    }
}



@Component({
    selector: 'book',
    directives: [PageComponent],
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: `
        <div id="pageScrollZone">
            <div id="pageHolder" [ngStyle]="pageHolderStyles()"></div>
        </div>
    `
})

export class BookComponent {
    constructor(
            private _cd: ChangeDetectorRef,
            private _playerService: PlayerService,
            private _config: Config
    ){}

    ngAfterContentChecked(){
// This part does execute when the observable changes
        let date = new Date();
        console.log('BOOK: '+date.getHours()+':'+date.getMinutes()+':'+date.getSeconds());
    }

    pageHolderStyles(){
// This part does NOT execute when the observable changes
        let styles = {
            marginTop: this._currentMargin.y+'px',
            transform: 'scale('+ (this._baseZoom * this._currentZoomLevel) +')'
        }

        return styles;
    }
}
4

1 に答える 1

2

すっごく... 2つの簡単なこと..

まず、使用するmarkForCheck()と、コンポーネントITSELFROOT COMPONENTの間に移動して、Angular が変更検出を実行する対象のパスをマークします。

優れたソートラムの記事から: 木

「markForCheck() という API が付属する依存性注入を介して、コンポーネントの ChangeDetectorRef にアクセスできます。このメソッドはまさに私たちが必要としているものを実行します!コンポーネントからルートまでのパスをマークし、次の変更検出実行のためにチェックします。」

したがって、トリガーすると、それ自体の子は表示されず、ルート コンポーネントが表示されます。その子も onPush である場合、更新は無視されます。

すべての子 (コンポーネント階層の数レベルの深さの場合もあります) で観察対象をリッスンすることになっていますか?

すべての要素を明示的に onPush にしたい場合のみ。私の推測では、親がそうする場合、ページが自動的に更新されるようにしたいということです...まあ、あなたは幸運です! ページ コンポーネントで標準戦略を使用し、ブックでonPush 戦略を使用すると、検出がツリーを下っていくときに、子が通常のコンポーネントであっても、そのブランチはスキップされます。出典:ビクター・サフキン

いいえ、オブザーバーに深く耳を傾ける必要はありません。休憩を取りたい場所ならどこでもかまいません。onPush であるすべてのコンポーネントは、それをだまして動作させる必要がある場合、パフォーマンスの向上を保証するものではありません。ほとんどの場合、重要なノードをブロックするだけで十分です。

2 つ目はngAfterContentCheckedに関するものです。 これは既知のバグです。

(まったく同じ) https://github.com/angular/angular/issues/7055

(関連する、両方のより良い議論) https://github.com/angular/angular/issues/7054

彼らはそれについて知っています、それは実際に検出を実行しているわけではありませんが、コールバックを起動しています...

于 2016-05-28T04:16:08.063 に答える