简体   繁体   English

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

[英]Using a base component to unsubscribe from all observable subscriptions

In an Angular app, we're using a Base Component to unsubscribe from most of our app's observable subscriptions.Angular应用程序中,我们使用Base Component取消订阅我们应用程序的大部分可观察订阅。 If a component subscribes to an observable, that component will extend the Base Component .如果一个组件订阅了一个 observable,该组件将extend Base Component My thought is that this keeps observable subscriptions alive until the entire application is destroyed , rather than until each component is destroyed:我的想法是,这会使可观察订阅保持活动状态,直到整个应用程序被destroyed ,而不是直到每个组件都被销毁:

base.component.ts:基础.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();
  }
}

the-rest-of-our-components.ts:我们的其余组件.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;
      });
  }

If many components extend BaseComponent and utilize its unsubscribe$ Subject, does that mean all of my subscriptions only get unsubscribed when the entire application is destroyed (aka user closes the tab or navigates away from web app, thus Base Component is destroyed), rather than when individual components get destroyed?如果许多组件扩展BaseComponent并使用它的unsubscribe$ Subject,这是否意味着我的所有订阅只会在整个应用程序被销毁时取消订阅(也就是用户关闭选项卡或导航离开 web 应用程序,因此Base Component被销毁),而不是个别组件何时被销毁?

Is this a strategy you've seen before, and is it advisable?这是您以前见过的策略吗?它是否可取? If it works as I'm assuming, this means all of our subscriptions across the application stay active until the whole app is destroyed.如果它像我假设的那样工作,这意味着我们在整个应用程序中的所有订阅都保持活动状态,直到整个应用程序被销毁。 I see how, depending on our needs, that might be a bad thing or a good thing.我明白,根据我们的需要,这可能是一件坏事,也可能是一件好事。

Bonus question: is Base Component going to act like a singleton ?奖励问题: Base Component是否会像singleton一样工作? AKA if multiple components simultaneously extend BaseComponent , will they all be using the same instance of unsubscribe$ or will there be multiple instances of unsubscribe$ (one per component)?也就是说,如果多个组件同时extend BaseComponent ,它们是否都使用同一个unsubscribe$实例,或者是否会有多个unsubscribe$实例(每个组件一个)?

I assumed this would work, but we all know where assumptions get you, so I made a test: https://stackblitz.com/edit/angular-ivy-ueshwz?file=src/app/extended/extended.component.ts我以为这行得通,但我们都知道你的假设在哪里,所以我做了一个测试: https://stackblitz.com/edit/angular-ivy-ueshwz?file=src/app/extended/extended.component.ts

It works, as in subscriptions get destroyed when individual components get destroyed.它有效,因为订阅在单个组件被销毁时被销毁。


We make a service that holds a subject we can subscribe to, and a value we can change with the subscription, to show that the subscription exists:我们创建一个服务,其中包含一个我们可以订阅的主题,以及一个我们可以随订阅更改的值,以表明订阅存在:

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

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

In the root we'll fire the subject every second, and have a component that we can toggle on and off在 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>

Then we'll use an extended component to tick that value up by 1 with a subscription然后我们将使用扩展组件通过订阅将该值加 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>

Neat, closing the component stops the ticker, so the subscription has been unsubscribed.整洁,关闭组件会停止自动收报机,因此订阅已取消订阅。


Bonus question: BaseComponent does not act like a singleton, when you create an instance of an object it does not create shared instances of parent classes.奖励问题: BaseComponent不像 singleton,当您创建 object 的实例时,它不会创建父类的共享实例。 Extending a class just adds properties and methods to that instance.扩展 class 只是向该实例添加属性和方法。


I'm not sure if I would recommend this, if someone overrides ngOnDestroy() they need to call super.ngOnDestroy() , which may be easy to forget.我不确定我是否会推荐这个,如果有人覆盖ngOnDestroy()他们需要调用super.ngOnDestroy() ,这可能很容易忘记。 It's only four lines of code, probably better to explicitly put it in every component that needs it.它只有四行代码,最好将它明确地放在每个需要它的组件中。 Manual subscriptions should be pretty rare anyway, if you're using the async pipe.如果您使用的是async pipe,手动订阅应该很少见。

I solved this in a project doing the following:我在执行以下操作的项目中解决了这个问题:

In base.component :base.component

private sub: any = {};

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

Then in any component that extends:然后在任何扩展的组件中:

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

With this method, the subscription stay active until the component is destroyed.使用此方法,订阅将保持活动状态,直到组件被销毁。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM