Angular 使用NgRx存储进行角度内存优化,以避免内存泄漏
我是Angular 11中企业应用程序的一部分。我们大量使用NgRx存储,我想知道是否可以通过优化订阅来改善应用程序内存占用(目前内存占用大约为~200-220 MB和live JS堆大小~76-85 MB) 对我们来说,性能优化非常重要,因为我们的客户端不会整天刷新或关闭浏览器窗口,而且因为我们有足够的订阅服务于我们的客户端,所以我需要帮助将内存占用保持在可接受的限制之下,并且不会影响我们的客户端 我们以以下方式使用订阅: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 &
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响应,并且这很不确定它何时将返回决定进一步执行业务逻辑的数据分派
- 还有其他的优化方法吗?(请注意,在我们的组件中,无论何时何地需要,我们都遵循前面提到的
语法,在处理NgRx存储、效果和可观察对象时,是否有其他方法防止内存消耗)takeUntil()
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很高兴能提供帮助!