使用基本组件取消订阅所有可观察订阅
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
管道,那么无论如何手动订阅应该很少见。
在 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
管道,那么无论如何手动订阅应该很少见。