简体   繁体   English

在 Angular 中渲染基于时间的 Observable,而无需进行大量的更改检测

[英]Render time-based Observables in Angular without overwhelming change detection

We have a number of components in our Angular application that need to regularly display new values every second that are unique to each component (countdowns, timestamps, elapsed time, etc).我们的 Angular 应用程序中有许多组件需要每秒定期显示每个组件独有的新值(倒计时、时间戳、经过时间等)。 The most natural way to is to create observables that use the RxJS timer and interval factory functions.最自然的方法是创建使用 RxJS timerinterval工厂函数的 observable。 However, these trigger Angular change detection at every interval for the entire app as many times as the interval function was called .但是,这些会在整个应用程序的每个时间间隔触发 Angular 变化检测,次数与调用时间间隔函数的次数相同 If we have dozens of components on the page, this triggers change detection for the entire app dozens of times every second or time period, creating a large performance overhead.如果页面上有几十个组件,这会在每秒或时间段内触发整个应用程序的数十次更改检测,从而产生很大的性能开销。

So far there are two ways I've attempted to solve the problem.到目前为止,我尝试了两种方法来解决这个问题。 A good answer to either would be very helpful - ideally both.对任何一个的一个好的答案都会非常有帮助 - 理想情况下两者。 I want to avoid manual trigger of change detection and instead rely on new values emitted from Observables and have the async pipe/OnPush change detection strategy be responsible for triggering change detection.我想避免手动触发更改检测,而是依赖于从 Observables 发出的新值,并让异步管道/OnPush 更改检测策略负责触发更改检测。 If this isn't possible, I'd like to understand why.如果这是不可能的,我想了解原因。

  1. Is there any way to disable or prevent RxJS timer or interval functions from triggering Angular change detection?有没有办法禁用或阻止 RxJS timerinterval函数触发 Angular 变化检测? Using NgZone zone.runOutsideAngular(() => this.interval$ = interval(1000) ... ) does not appear to do this.使用 NgZone zone.runOutsideAngular(() => this.interval$ = interval(1000) ... )似乎不会这样做。 StackBlitz example: https://stackblitz.com/edit/angular-zo5h39 StackBlitz 示例: https ://stackblitz.com/edit/angular-zo5h39
  2. Alternatively, if I create an Observable stream using an RxJS Subject combined with setInterval called inside zone.runOutsideAngular , why isn't change detection triggered for the child component when a new value is emitted from the subject?或者,如果我使用 RxJS SubjectsetInterval组合创建一个 Observable 流,称为 inside zone.runOutsideAngular ,为什么当从主题发出新值时不会为子组件触发更改检测? StackBlitz example: https://stackblitz.com/edit/angular-yxdjgd 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?有没有办法禁用或阻止 RxJS 定时器或间隔函数触发 Angular 变化检测? Using NgZone zone.runOutsideAngular(() => this.interval$ = interval(1000) ... ) does not appear to do this.使用 NgZone zone.runOutsideAngular(() => this.interval$ = interval(1000) ... ) 似乎不会这样做。

It's because observables are cold and the value producer (setInterval) is only executed when a subscription happens.这是因为 observable 是冷的,并且值生产者 (setInterval) 仅在订阅发生时执行。 Read more here .在这里阅读更多。 Although this code is executed outside of Angular zone:尽管此代码是在 Angular zone 之外执行的:

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

it doesn't actually immediately invoke the value producer inside interval operator.它实际上并没有立即调用interval运算符内的值生产者。 It will be later invoked when AsyncPipe in the IntervalInnerComponent subscribes.稍后会在IntervalInnerComponent AsyncPipe 订阅时调用。 And this happens in inside Angular zone:这发生在 Angular 区域内:

在此处输入图片说明

Using subject in the next alternative you make it hot and subscribe immediately outside of Angular zone.在下一个替代方案中使用主题,您可以使其变得热门并立即在 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或者,如果我使用 RxJS 主题结合 setInterval 创建一个 Observable 流,称为 inside zone.runOutsideAngular... setInterval 值正在创建(它们可以手动订阅),但异步管道似乎不会触发更改检测

Because Async Pipe doesn't trigger change detection.因为异步管道不会触发更改检测。 It simply marks a component and all its ancestors for check during the following change detection whenever it will be.它只是简单地标记一个组件及其所有祖先,以便在接下来的更改检测期间随时进行检查。 See this answer or this article for more details.有关更多详细信息,请参阅此答案本文 Zone.js triggers change detection. Zone.js 触发变更检测。 But since you're running setInterval outside Angular zone, it doesn't trigger it.但是由于您在 Angular 区域之外运行setInterval ,它不会触发它。 Under this setup you have a few options:在此设置下,您有几个选项:

  • inject ChangeDetectorRef and manually trigger change detection for the component and its ancestors注入ChangeDetectorRef并手动触发组件及其祖先的更改检测
  • inject ApplicationRef and manually trigger change detection for the entire application注入ApplicationRef并手动触发整个应用程序的更改检测

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM