我注意到Angular中一个奇怪的变化检测行为。当Observable像例子中那样更新时,由于某些原因没有触发变更检测。
这里的关键是回调内部调用的setTimeout
,如果您删除它,更改检测将正常工作。markForCheck
里面的AsyncPipe
也应该叫
@Component({
selector: 'my-app',
template:
'<button (click)="click()">Trigger</button> <br> {{value$ | async}}',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {
readonly value$ = new BehaviorSubject(1);
constructor(
private readonly zone: NgZone,
) {}
click() {
this.zone.runOutsideAngular(() => {
setTimeout(() => {
this.value$.next(this.value$.value + 1);
console.log(`Change (Should be ${this.value$.value})`);
});
});
}
}
StackBlits例子
StackBlitz与异步管道调试示例
结果:
点击触发一个CD循环,并标记OnPush组件要被检查。超时启动。检查组件的更改,但值尚未更改,因此UI未更新。CD循环完成
超时白白的流逝了。它不会触发另一个CD循环,因为它在runOutsideAngular
内部。值是下一个。Async pipe调用markForCheck
,但这不会有任何可见的效果,直到另一个CD周期被触发,例如,通过点击另一个按钮。
如果删除setTimeout
,则该值将在下一个由点击按钮触发的CD周期中,并且UI将被更新。
如果runOutsideAngular
被删除,超时触发一个新的CD循环,组件将被检查更改,因为异步管道调用markForCheck
,并且UI将被更新。