Angular2 可观察变化检测模板更新

Angular2 observable change detection template update

出于性能原因,我尝试在我的组件上设置手动更改检测。

应用程序的结构:应用程序 -> 书籍 -> 页面

我在 AppComponent 中订阅了一个可观察对象,然后我 运行 ChangeDetectorRef 的 "markForCheck" 方法。

这似乎触发了 BookComponent 中的 ngAfterContentChecked 方法(我标记为更新的组件的 child)。

这让我相信 parent 组件中的 "markForCheck" 也会更新所有 children。

然而,即使在 child (BookComponent) 中执行了 ngAfterContentChecked,该组件的模板也不会更新!

我是否应该在每个 child 中监听 observable(有时在组件层次结构中的几个层次)?还是我做错了什么?为什么 ngAfterContentChecked 在 child 组件中执行,即使模板没有更新?

一些相关代码。

@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;
    }
}

Sooo...两件快事..

首先,当您使用 markForCheck() 时,它会在组件 ITSELFROOT COMPONENT 之间标记一个路径Angular 应该 运行 更改检测。

From a great Thoughtram Article:

"We can access a component’s ChangeDetectorRef via dependency injection, which comes with an API called markForCheck(). This method does exactly what we need! It marks the path from our component until root to be checked for the next change detection run."

因此当您触发时它不会查看它自己的 children,它会查看根组件。如果它的 children 也是 onPush 他们将忽略它的更新。

Am I supposed to listen to the observable in every child (sometimes several levels deep in the component hierarchy)?

仅当您希望所有元素都显式 onPush 时。 我的猜测是您希望 Pages 自动更新,如果 parent 确实如此……那么您很幸运!如果在页面组件上使用标准策略并在检测时在Book上使用onPush策略 运行 沿着树向下它会 跳过 那个分支,即使 children 是普通组件。 Source:Victor Savkin

所以NO你不必深入倾听观察者的声音,只要你想休息的地方就可以了。 如果您必须欺骗它才能工作,则每个处于 onPush 状态的组件都不能保证性能提升。在大多数情况下,只阻塞关键节点就足够了。

第二件事是关于ngAfterContentChecked 这是一个已知错误:

(完全相同)https://github.com/angular/angular/issues/7055

(相关,更好地讨论两者)https://github.com/angular/angular/issues/7054

所以他们知道了,它实际上不是 运行ning 检测,而是触发回调...