如何解决使用 NgRx 存储进行角度内存优化以避免内存泄漏
我是 Angular 11 企业应用程序的一部分。我们大量使用 NgRx 存储,我想知道我们是否可以通过优化订阅来改善我们的应用程序内存占用(目前内存占用是大约 ~200-220 MB 和 实时 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();
}
同样,我们还有许多其他可观察订阅,用于监听 App State 的不同部分。我有几个疑问:
- 订阅
this.accountProgress$.subscribe(() => {...})
是否仍会导致内存泄漏?如果在同一个组件中多次使用它以不同的方法获取数据?对于这种订阅,我们是否也应该使用takeuntil()
或take(1)
(我们在几个地方有这个,但我不确定它是否有帮助)?或者将publish() + refCount()
或share({refCount: true})
等共享运算符与this.store.pipe(select(appState => appState.account.accountProgress))
一起使用? - 有时我们需要根据我们在订阅中收到的值
dispatch
一个操作,我知道这是一种反模式,我们不应该dispatch
订阅中的操作,但可以做什么,因为很少有存储依赖 API 响应,并且它何时返回决定业务逻辑的进一步执行的数据相当不确定 - 还有其他优化方法吗? (请注意,我们在组件中需要的任何地方都遵循上述
takeuntil()
语法,在处理 NgRx store、Effects 和 Observables 时,是否还有其他方法可以防止内存消耗)
解决方法
您处理订阅的实际方法很棒,但我认为在 Angular 中处理 rxjs 代码的最佳方法是使用异步管道和良好的数据组合。
您必须考虑渲染组件所需的所有数据,并将其组合起来,以便在 HTML 中订阅一个最终的 observable,这样您就可以利用 ChangeDetectionStrategy
和 trackBy
对于 *ngFor
具有反应性数据。
点击这些链接以获得更好的理解:
希望对你有所帮助。
,订阅 this.accountProgress$.subscribe(() => {...})
仍会导致内存泄漏吗?
不久前,我写了here一个回答,描述了在 RxJS 中使用 Subject 时为什么会发生内存泄漏。因此,在这种情况下,我会说它不会,因为您使用的是 takeUntil(this.destroyed$)
。
如果在同一个组件内多次使用它以不同的方法获取数据?
如果 takeUntil
仍然存在,则不会有任何内存泄漏。但是,您应该注意一个重要方面。
假设您的主题有几个订阅者:
const s = new Subject();
const o1$ = s.pipe(
a(),b(),c(),d(),);
const o2$ = s.pipe(
a(),);
如果订阅了 o1$
3 次,那么直到 s.pipe
之前,每个运算符都会有 3 个不同的实例,即 a
、{{1} }、b
、c
。这可能是您想要避免的,您可以通过使用 d
,share()
运算符来实现:
shareReplay()
对于此类订阅,我们是否也应使用 const o1$ = s.pipe(
a(),share(),);
// now,when `o1$` is subscribed,each of the `a-d` operators will have exactly one instance
o1$.subscribe()
o1$.subscribe()
o1$.subscribe()
或 takeUntil()
这取决于,take(1)
将在 take(n)
值发出后完成,而 n-th
将在 takeUntil(notifier$)
发出后完成。
有时我们需要根据我们在订阅中收到的值来分派一个动作......
也许不是在订阅中执行此操作,您可以使用 notifier$
运算符,这是众所周知的副作用。
tap
此外,上面的 data$ = this.store.select(yourSelector).pipe(
/* ... */
tap(result => this.store.dispatch(anAction))
/* ... */
)
observable 可以与异步管道结合使用,因此您不必处理手动订阅/取消订阅。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。