在 Angular 组件中管理订阅的 DRY 方法

2025-06-10

在 Angular 组件中管理订阅的 DRY 方法

封面照片由Tim Swaan在 Unsplash 上拍摄。

本文解释了如何在 Angular 组件中管理订阅,而无需在每个组件中重复相同的拆卸逻辑。

常用方法

有两种常用方法来管理 Angular 组件中的 RxJS 订阅以防止内存泄漏:

使用订阅

@Component({
  selector: 'interval',
  templateUrl: './interval.component.html',
})
export class IntervalComponent implements OnInit, OnDestroy {
  // initialize `Subscription` object
  private readonly subscriptions = new Subscription();

  ngOnInit(): void {
    // add all subscriptions to it
    this.subscriptions.add(
      interval(1000)
        .pipe(map(i => `== ${i} ==`))
        .subscribe(console.log)
    );

    this.subscriptions.add(
      interval(2000)
        .pipe(map(i => `=== ${i} ===`))
        .subscribe(console.log)
    );
  }

  ngOnDestroy(): void {
    // unsubscribe from all added subscriptions
    // when component is destroyed
    this.subscriptions.unsubscribe();
  }
}
Enter fullscreen mode Exit fullscreen mode

使用 Destroy Subject

@Component({
  selector: 'interval',
  templateUrl: './interval.component.html',
})
export class IntervalComponent implements OnInit, OnDestroy {
  // initialize destroy subject
  private readonly destroySubject$ = new Subject<void>();

  ngOnInit(): void {
    interval(1000)
      .pipe(
        map(i => `== ${i} ==`),
        // unsubscribe when destroy subject emits an event
        takeUntil(this.destroySubject$)
      )
      .subscribe(console.log);

    interval(2000)
      .pipe(
        map(i => `=== ${i} ===`),
        takeUntil(this.destroySubject$)
      )
      .subscribe(console.log);
  }

  ngOnDestroy(): void {
    // emit destroy event when component is destroyed
    this.destroySubject$.next();
  }
}
Enter fullscreen mode Exit fullscreen mode

这两种解决方案都有相同的缺点:我们必须初始化附加属性,并在ngOnDestroy方法中添加拆卸逻辑。然而,在 Angular 组件中,有一种更好的方法来管理订阅。

解决方案

Destroy我们可以通过创建扩展类Observable并实现接口的类将拆卸逻辑放在一个地方OnDestroy

@Injectable()
export class Destroy extends Observable<void> implements OnDestroy {
  // initialize destroy subject
  private readonly destroySubject$ = new ReplaySubject<void>(1);

  constructor() {
    // emit destroy event to all subscribers when destroy subject emits
    super(subscriber => this.destroySubject$.subscribe(subscriber));
  }

  ngOnDestroy(): void {
    // emit destroy event when component that injects
    // `Destroy` provider is destroyed
    this.destroySubject$.next();
    this.destroySubject$.complete();
  }
}
Enter fullscreen mode Exit fullscreen mode

然后,我们可以Destroy在组件级别提供并通过构造函数注入它:

@Component({
  // provide `Destroy` at the component level
  viewProviders: [Destroy]
})
export class IntervalComponent implements OnInit {
  // inject it through the constructor
  constructor(private readonly destroy$: Destroy) {}

  ngOnInit(): void {
    interval(1000)
      .pipe(
        map(i => `== ${i} ==`),
        // unsubscribe when `destroy$` Observable emits an event
        takeUntil(this.destroy$)
      )
      .subscribe(console.log);
  }
}
Enter fullscreen mode Exit fullscreen mode

当提供程序在组件级别提供时,它将与组件生命周期绑定,这允许我们使用ngOnDestroy其中的生命周期方法。因此,当组件被销毁时,提供程序ngOnDestroy的方法Destroy将被调用。IntervalComponent

结论

一般来说,应该避免在 Angular 组件中手动订阅/取消订阅。如果需要在组件级别执行副作用,可以使用@ngrx/component-store效果来实现,并注意ComponentStore防止内存泄漏。但是,如果您更喜欢在组件中管理副作用,请考虑使用Destroy提供程序,以避免在每个组件中重复相同的拆卸逻辑。

同行评审员

鏂囩珷鏉ユ簮锛�https://dev.to/this-is-angular/dry-way-to-manage-subscriptions-in-angular-components-256j
PREV
Angular 的模型-视图-呈现器 复杂应用程序 关注点分离 模型-视图-呈现器模式 Angular 的模型-视图-呈现器概念 容器组件 混合组件 呈现器 模型-视图-呈现器三角组合 改进的 Angular 应用程序 案例研究:英雄之旅 资源 相关文章 致谢 同行评审员
NEXT
使用 Motion One 为你的 Angular 应用制作动画