在 Angular 中渲染基于时间的 Observables 而没有压倒性的变化检测

Render time-based Observables in Angular without overwhelming change detection

我们的 Angular 应用程序中有许多组件需要每秒定期显示每个组件唯一的新值(倒计时、时间戳、经过时间等)。最自然的方法是创建使用 RxJS timerinterval 工厂函数的可观察对象。但是,这些触发 Angular 整个应用的每个时间间隔的更改检测 与调用时间间隔函数的次数相同 。如果我们在页面上有几十个组件,这会触发整个应用程序每秒或每个时间段数十次的变化检测,从而产生很大的性能开销。

到目前为止,我尝试了两种方法来解决问题。对任何一个的好的回答都会非常有帮助——最好是两者兼而有之。我想避免手动触发变更检测,而是依赖于 Observables 发出的新值,并让异步 pipe/OnPush 变更检测策略负责触发变更检测。如果这不可能,我想了解原因。

  1. 有什么方法可以禁用或阻止 RxJS timerinterval 函数触发 Angular 变化检测吗?使用 NgZone zone.runOutsideAngular(() => this.interval$ = interval(1000) ... ) 似乎无法做到这一点。 StackBlitz 示例:https://stackblitz.com/edit/angular-zo5h39
  2. 或者,如果我使用 RxJS Subject 结合在 zone.runOutsideAngular 内部调用的 setInterval 创建一个 Observable 流,为什么当一个新的价值是从主题发出的? StackBlitz 示例:https://stackblitz.com/edit/angular-yxdjgd
  1. Is there any way to disable or prevent RxJS timer or interval functions from triggering Angular change detection? Using NgZone zone.runOutsideAngular(() => this.interval$ = interval(1000) ... ) does not appear to do this.

这是因为 observables 是冷的,值生成器 (setInterval) 仅在订阅发生时执行。阅读更多 here。尽管此代码在 Angular 区域之外执行:

() => this.interval$ = interval(1000) ...

它实际上并没有立即调用 interval 运算符中的值生成器。稍后将在 IntervalInnerComponent 中的 AsyncPipe 订阅时调用。这发生在 Angular 区域内:

在下一个替代方案中使用主题,您可以使其成为热点并立即在 Angular 区域之外订阅。

  1. Alternatively, if I create an Observable stream using an RxJS Subject combined with setInterval called inside zone.runOutsideAngular... setInterval values are being created (they can be subscribed to manually), but the async pipe does not appear to be triggering change detection

因为异步管道不会触发更改检测。它只是标记一个组件及其所有祖先,以便在接下来的更改检测期间随时进行检查。有关详细信息,请参阅此 or this article。 Zone.js 触发变化检测。但是因为你 运行 setInterval 在 Angular 区域之外,它不会触发它。在此设置下,您有几个选项:

  • 注入 ChangeDetectorRef 并手动触发组件及其祖先的更改检测
  • 注入 ApplicationRef 并手动触发整个应用程序的更改检测