使用基本组件取消订阅所有可观察订阅

Using a base component to unsubscribe from all observable subscriptions

Angular 应用中,我们使用 Base Component 取消订阅我们应用的大部分可观察订阅。如果一个组件订阅了一个可观察对象,该组件将 extend Base Component。我的想法是,这会使可观察订阅保持活动状态,直到整个应用程序 destroyed,而不是直到每个组件都被销毁:

base.component.ts:

import { Subject } from 'rxjs';
import { OnDestroy, Component } from '@angular/core';

export abstract class BaseComponent implements OnDestroy {
  protected unsubscribe$ = new Subject<void>();

  ngOnDestroy(): void {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
}

我们的其他人components.ts:

import { Component, OnInit } from '@angular/core';
import { MyService } from 'src/app/services/my.service';
import { BaseComponent } from '../base/component/base-component';

export class myComponent extends BaseComponent implements OnInit {
  myProperty: string;

  constructor(
    private myService: MyService,
  ) {
    super();
  }
  ngOnInit(): void {
    this.myService.doStuff$
      .pipe(takeUntil(this.unsubscribe$)) // take until baseComponent's unsubscribe$
      .subscribe((data) => {
        this.myProperty = data;
      });
  }

如果许多组件扩展 BaseComponent 并利用其 unsubscribe$ 主题,这是否意味着我的所有订阅只会在整个应用程序被销毁时取消订阅(也就是用户关闭选项卡或导航离开web 应用程序,因此 Base Component 被销毁),而不是当单个组件被销毁时?

这是您以前见过的策略吗?它是否可取?如果它像我假设的那样工作,这意味着我们在整个应用程序中的所有订阅都保持活动状态,直到整个应用程序被销毁。我明白,根据我们的需要,这可能是一件坏事,也可能是一件好事。

奖金问题: Base Component 会像 singleton 一样行事吗?也就是,如果多个组件同时 extend BaseComponent,它们会全部使用 unsubscribe$ 相同 实例还是会有 [=18] 的多个实例=](每个组件一个)?

我在执行以下操作的项目中解决了这个问题:

base.component中:

private sub: any = {};

ngOnDestroy() {
  Object.keys(this.sub).map(item => {
     this.sub[item].unsubscribe();
  })
}

然后在任何扩展的组件中:

this.sub.myService = this.myService.doStuff$.subscribe(......

使用此方法,订阅将保持活动状态,直到组件被销毁。

我认为这会奏效,但我们都知道你的假设在哪里,所以我做了一个测试:https://stackblitz.com/edit/angular-ivy-ueshwz?file=src/app/extended/extended.component.ts

它有效,因为当单个组件被销毁时,订阅也会被销毁。


我们制作了一个服务,其中包含一个我们可以订阅的主题,以及一个我们可以随订阅更改的值,以表明订阅存在:

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/internal/Subject';

@Injectable({ providedIn: 'root' })
export class UpdateService {
  subject = new Subject<void>();
  value = 0;
}

在 root 中,我们将每秒触发主题,并有一个我们可以打开和关闭的组件

export class AppComponent implements OnInit {
  extCompOpen = true;

  constructor(public update: UpdateService) {}

  ngOnInit() {
    interval(1000).subscribe(() => this.update.subject.next());
  }
}
<app-extended *ngIf="extCompOpen"></app-extended>
<button (click)="extCompOpen = !extCompOpen">Toggle Component</button>

<p>This counter will keep going up as long as the subscription exists:</p>
<p>{{ update.value }}</p>

然后我们将使用扩展组件通过订阅将该值加 1

export class ExtendedComponent extends BaseComponent implements OnInit {
  constructor(private update: UpdateService) {
    super();
  }

  ngOnInit() {
    this.update.subject.pipe(takeUntil(this.unsubscribe$)).subscribe(() => {
      this.update.value++;
    });
  }
}
<p>Extended component exists!</p>

很好,关闭组件会停止自动收报机,因此订阅已取消订阅。


奖励问题:BaseComponent 不像单例,当您创建一个对象的实例时,它不会创建父 classes 的共享实例。扩展 class 只是向该实例添加属性和方法。


我不确定我是否会推荐这个,如果有人覆盖 ngOnDestroy() 他们需要调用 super.ngOnDestroy(),这可能很容易忘记。它只有四行代码,最好将它明确地放在每个需要它的组件中。如果您使用的是 async 管道,那么无论如何手动订阅应该很少见。