我是 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))
? - 有时我们需要根据订阅中收到的值
分发
一个 Action ,我知道这是一个反模式,我们不应该在订阅中分派(dispatch)
一个 Action ,但是可以做什么,因为很少有商店依赖于 API 响应,而且它何时返回数据将决定业务逻辑的进一步执行是非常不确定的 - 还有其他优化方法吗? (请注意,我们在任何需要的组件中都遵循上述
takeUntil()
语法,在处理 NgRx 存储、Effects 和 Observable 时是否还有其他方法可以防止内存消耗)
最佳答案
Can the subscription
this.accountProgress$.subscribe(() => {...})
still cause a memory leak?
前一段时间,我写了here描述为什么在 RxJS 中使用 Subject 时会发生内存泄漏的答案。所以,在这种情况下,我会说它不会,因为你正在使用 takeUntil(this.destroyed$)
。
If it is used multiple times within the same component to fetch data in different methods?
如果 takeUntil
仍然存在,则不会有任何内存泄漏。但是,有一个重要方面您应该注意。
假设你有一个有几个订阅者的主题:
const s = new Subject();
const o1$ = s.pipe(
a(),
b(),
c(),
d(),
);
const o2$ = s.pipe(
a(),
b(),
);
如果您订阅了 o1$
3 次,在 s.pipe
之前,每个运算符将有 3 个不同的实例,即 a
、b
、c
、d
。这可能是您想要避免的事情,您可以通过使用 share()
、shareReplay()
运算符来实现:
const o1$ = s.pipe(
a(),
b(),
c(),
d(),
share(),
);
// now, when `o1$` is subscribed, each of the `a-d` operators will have exactly one instance
o1$.subscribe()
o1$.subscribe()
o1$.subscribe()
Should we use
takeUntil()
ortake(1)
with this kind of subscriptions as well
视情况而定,take(n)
将在 n-th
值发出后完成,takeUntil(notifier$)
将完成当 notifier$
发出时。
Sometimes we need to dispatch an action based on the value we receive within the subscription ...
也许您可以使用 tap
运算符,而不是在订阅中执行此操作,这是众所周知的副作用。
data$ = this.store.select(yourSelector).pipe(
/* ... */
tap(result => this.store.dispatch(anAction))
/* ... */
)
此外,上面的 data$
observable 可以与异步管道结合使用,因此您不必处理手动订阅/取消订阅。
关于使用 NgRx store 进行 Angular 内存优化以避免内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66075764/