3

我是 Angular 11 中企业应用程序的一部分。我们大量使用 NgRx 存储,我想知道我们是否可以通过优化订阅来改善我们的应用程序内存占用(目前内存占用约为200-220 MB实时 JS Heap大小〜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()or share({refCount: true})with this.store.pipe(select(appState => appState.account.accountProgress))?
  • 有时我们需要dispatch根据我们在订阅中收到的值来执行操作,我知道这是一种反模式,我们不应该dispatch在订阅中执行操作,但是可以做什么,因为很少有商店依赖 API 响应,并且不确定何时返回将决定业务逻辑进一步执行的数据
  • 还有其他优化方法吗?(请注意,我们在组件中任何需要的地方都遵循上述语法,takeUntil()在处理 NgRx 存储、效果和 Observables 时是否有任何其他方法可以防止内存消耗)
4

2 回答 2

1

订阅this.accountProgress$.subscribe(() => {...})还会导致内存泄漏吗?

前段时间,我在这里写了一个答案,描述了为什么在 RxJS 中使用 Subject 时会发生内存泄漏。所以,在这种情况下,我会说它不会,因为你正在使用takeUntil(this.destroyed$).

如果在同一个组件中多次使用它以不同的方法获取数据?

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

const s = new Subject();

const o1$ = s.pipe(
  a(),
  b(),
  c(),
  d(),
);

const o2$ = s.pipe(
  a(),
  b(),
);

如果您订阅o1$3 次,每个运算符将有 3 个不同的实例使用 until s.pipe,即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()

我们是否也应该使用takeUntil()take(1)与这种订阅一起使用

这取决于,take(n)将在n-th发出值后完成,并在发出takeUntil(notifier$)时完成notifier$

有时我们需要根据我们在订阅中收到的值来调度一个动作......

tap也许您可以使用以副作用而闻名的运算符,而不是在订阅中执行此操作。

data$ = this.store.select(yourSelector).pipe(
  /* ... */
  tap(result => this.store.dispatch(anAction))
  /* ... */
)

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

于 2021-02-09T10:00:53.500 回答
1

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

您必须考虑渲染组件所需的所有数据,并将其组合起来以获得最终的 observable 以在 HTML 中订阅,这样您就可以利用ChangeDetectionStrategytrackBy使用*ngFor响应式数据。

请点击以下链接以获得更好的理解:

希望我对你有一点帮助。

于 2021-02-06T19:28:30.273 回答