Angular 使用NgRx存储进行角度内存优化,以避免内存泄漏

Angular 使用NgRx存储进行角度内存优化,以避免内存泄漏,angular,memory-leaks,rxjs,ngrx-store,ngrx-effects,Angular,Memory Leaks,Rxjs,Ngrx Store,Ngrx Effects,我是Angular 11中企业应用程序的一部分。我们大量使用NgRx存储,我想知道是否可以通过优化订阅来改善应用程序内存占用(目前内存占用大约为~200-220 MB和live JS堆大小~76-85 MB) 对我们来说,性能优化非常重要,因为我们的客户端不会整天刷新或关闭浏览器窗口,而且因为我们有足够的订阅服务于我们的客户端,所以我需要帮助将内存占用保持在可接受的限制之下,并且不会影响我们的客户端 我们以以下方式使用订阅: private destroyed$: ReplaySubject &

我是Angular 11中企业应用程序的一部分。我们大量使用NgRx存储,我想知道是否可以通过优化订阅来改善应用程序内存占用(目前内存占用大约为~200-220 MBlive JS堆大小~76-85 MB

对我们来说,性能优化非常重要,因为我们的客户端不会整天刷新或关闭浏览器窗口,而且因为我们有足够的订阅服务于我们的客户端,所以我需要帮助将内存占用保持在可接受的限制之下,并且不会影响我们的客户端

我们以以下方式使用订阅:

private destroyed$: ReplaySubject <boolean> = new ReplaySubject(1);


ngOnInit(): void {
    this.accountProgress$ = this.store.pipe(select(appState => appState.account.accountProgress), takeUntil(this.destroyed$));
    ...
    ...
    this.accountProgress$.subscribe(() => {...})
}

ngOnDestroy(): void {
    this.destroyed$.next(true);
    this.destroyed$.complete();
}
private$:ReplaySubject=新的ReplaySubject(1);
ngOnInit():void{
this.accountProgress$=this.store.pipe(选择(appState=>appState.account.accountProgress),takeUntil(this.destrocted$);
...
...
this.accountProgress$.subscribe(()=>{…})
}
ngOnDestroy():void{
此.next(true)已销毁$;
此.complete$.complete();
}
同样,我们还有许多其他可观察的订阅,它们监听应用程序状态的不同部分。我有几点疑问:

  • 订阅
    this.accountProgress$.subscribe(()=>{…})
    是否仍会导致内存泄漏?是否在同一组件中多次使用它以不同的方法获取数据?我们应该在这种订阅中使用
    takeUntil()
    还是
    take(1)
    (我们在少数地方有这种订阅,但我不确定它是否有用)?或者使用共享操作符,例如
    publish()+refCount()
    share({refCount:true})
    this.store.pipe(选择(appState=>appState.account.accountProgress))
  • 有时我们需要根据订阅中收到的值
    分派
    一个操作,我知道这是一个反模式,我们不应该
    分派
    订阅中的一个操作,但是可以做什么,因为只有很少的存储依赖于API响应,并且这很不确定它何时将返回决定进一步执行业务逻辑的数据
  • 还有其他的优化方法吗?(请注意,在我们的组件中,无论何时何地需要,我们都遵循前面提到的
    takeUntil()
    语法,在处理NgRx存储、效果和可观察对象时,是否有其他方法防止内存消耗)

处理订阅的实际方法很棒,但我认为在Angular中处理rxjs代码的最佳方法是使用异步管道和良好的数据组合

您必须考虑呈现组件所需的所有数据,并将其组合起来,以仅获得一个最终可观察的HTML订阅,这样您就可以利用
ChangeDetectionStrategy
trackBy
for
*ngFor
和反应式数据

通过以下链接获得更好的理解:

希望我能帮你一点忙

订阅
this.accountProgress$.subscribe(()=>{…})
是否仍会导致内存泄漏

不久前,我写了一个答案,描述了为什么在RxJS中使用主题时会发生内存泄漏。因此,在本例中,我认为不会,因为您使用的是
takeUntil(this.destromed$)

是否在同一组件中多次使用它以不同的方法获取数据

如果
takeUntil
仍然存在,则不会出现任何内存泄漏。但是,有一个重要的方面需要注意。
假设您有一个主题有几个订户:

const s=新主题();
常数o1$=s.pipe(
a(),
b(),
c(),
d(),
);
常数o2$=s.管道(
a(),
b(),
);
如果您订阅了3次
o1$
,则在
s.pipe
之前将有3个不同的操作符实例,即
a
b
c
d
。这可能是您希望避免的,您可以通过使用
share()
shareReplay()
操作符来避免:

const o1$=s.pipe(
a(),
b(),
c(),
d(),
share(),
);
//现在,当订阅“o1$”时,每个“a-d”操作符将正好有一个实例
o1$.subscribe()
o1$.subscribe()
o1$.subscribe()
我们应该在这种订阅中使用
takeUntil()
还是
take(1)

视情况而定,
take(n)
将在发出
n个
值后完成,
takeUntil(notifier$)
将在发出
notifier$
时完成

有时,我们需要根据订阅中收到的值发送操作

您可以使用
tap
操作符,而不是在订阅中执行此操作,这是众所周知的副作用

data$=this.store.select(您的选择器).pipe(
/* ... */
点击(结果=>this.store.dispatch(操作))
/* ... */
)

此外,从上面可以观察到的
数据$
可以与异步管道一起使用,因此您不必处理手动订阅/取消订阅。

TakeTill很好,以防您试图消除内存泄漏。问题出在其他方面。在订阅内部调度操作可能更适合移动到效果。无论如何,它不会影响性能,但至少看起来更干净。我注意到,如果启用Redux开发工具扩展,应用程序中的“内存泄漏”会增加。尝试禁用它,看看禁用后的性能如何。@Andrei感谢您的建议。@AliF50共享的统计数据在没有任何redux扩展的正常系统上:)谢谢您的深入解释:)@Andreii很高兴能提供帮助!