【问题标题】:Is it really better to use async-pipe instead of subscribe() in this particular situation?在这种特殊情况下使用 async-pipe 而不是 subscribe() 真的更好吗?
【发布时间】:2026-02-16 07:50:01
【问题描述】:

在我的组件中,我得到一个 Observable 作为 @Input()。

如果 Observable 的值发生变化,我必须展开/折叠 ng-bootstrap 手风琴。所以我真的不需要模板中的 Observable 的值。

这是代码,我已经有了:

组件:

@Input() urlParam$!: Observable<string>;

@ViewChild('acc') accordion!: NgbAccordion;

urlParamChange$!: Observable<string>;

ngAfterViewInit(): void {
  this.urlParamChange$ = this.urlParam$.pipe(
    tap(urlParam => this.synchronizeAccordion(urlParam))
  );
}

synchronizeAccordion(urlParam: string): void {
// do something with this.accordion
}

在模板中,我通过异步管道订阅了新创建的 urlParamChange$:

<ng-container *ngIf="(urlParamChange$ | async)"></ng-container>

<ngb-accordion #acc="ngbAccordion">
 ....
</ngb-accordion>

由于我真的不需要urlParamChange$的值,所以我把它放在一个自己的ng-container中,这样完全独立,但是仍然会调用同步手风琴的方法。

我认为,上面的实现并不是那么好。您将操作拆分为两个不同的文件(需要组件中的 tap + 模板中的异步管道)。

下面的方式不是更好吗? 我不会在异步管道的帮助下订阅,而是直接在 ngAfterViewInit() 中订阅()。这样我就可以删除额外的变量 urlParamChange$ 并且实现全部在 ngAfterViewInit() 方法中。

组件:

@Input() urlParam$!: Observable<string>;

@ViewChild('acc') accordion!: NgbAccordion;

ngAfterViewInit(): void {
  this.urlParam$.subscribe(urlParam => this.synchronizeAccordion(urlParam));
}

synchronizeAccordion(urlParam: string): void {
// do something with this.accordion
}

并且在模板中额外的 ng-container 被移除:

<ngb-accordion #acc="ngbAccordion">
 ....
</ngb-accordion>

在我看来,两种实现都应该以完全相同的方式工作。根据最佳实践,我认为应该首选第一个解决方案。但第二个是更清洁的 IMO。

那么实现这种用例的最佳方式是什么?

【问题讨论】:

  • 我认为你是对的。有人告诉过你要另辟蹊径吗?
  • 不,我现在没有那么多经验,所以我不确定该使用哪一个。那么解决方案 #1 是首选方式吗?
  • 可以像我一样使用“虚拟 ng-container”吗?哪个没有实际意义,只能通过异步管道订阅 observable?
  • 我指的是第二个,你认为更干净的那个。使用异步管道是“最佳实践”的唯一原因是您不必手动取消订阅,因此它通常会变得更干净。但是,如果像您的情况一样,它不能使它更干净,您可能应该使用手动订阅/取消订阅实现。什么是“最佳实践”,什么不是“最佳实践”始终是一种意见,没有什么可以虔诚地遵循。了解最佳实践背后的原因,如果它对您的情况没有意义,请不要遵循它。

标签: angular typescript rxjs


【解决方案1】:

我建议你采用方法 2。通过你自己的承认,它似乎更清晰,更容易遵循。

想象将来有人出现,他们看到一个随机的 ng-container 带有 async 绑定但没有内容。如果不再使用它,他们很可能会决定将其删除,在这种情况下,您的整个 tap 操作将不再运行,并且不会抛出任何错误来提醒您这一点。

而通读方法 2 的人可以很容易地看到正在发生的事情,并且修改订阅的结果是显而易见的。

手动退订没有错,你只需要确保你这样做!

【讨论】:

    【解决方案2】:

    为什么您将第一个解决方案称为“最佳实践”?在您的情况下,它没有任何意义,请使用#2。

    【讨论】:

    • 因为第一个解决方案他不需要手动取消订阅
    • 没错,#2 我需要手动取消订阅。
    • 如果您不必手动取消订阅,我会说它遵循最佳做法。
    • 为了避免手动取消订阅,您建议 1) 引入另一个 observable 2) 使用模板中的异步管道订阅它。损害可读性从未被视为最佳做法。