在 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();
}
}
使用 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();
}
}
这两种解决方案都有相同的缺点:我们必须初始化附加属性,并在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();
}
}
然后,我们可以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);
}
}
当提供程序在组件级别提供时,它将与组件生命周期绑定,这允许我们使用ngOnDestroy
其中的生命周期方法。因此,当组件被销毁时,提供程序ngOnDestroy
的方法Destroy
将被调用。IntervalComponent
结论
一般来说,应该避免在 Angular 组件中手动订阅/取消订阅。如果需要在组件级别执行副作用,可以使用@ngrx/component-store
效果来实现,并注意ComponentStore
防止内存泄漏。但是,如果您更喜欢在组件中管理副作用,请考虑使用Destroy
提供程序,以避免在每个组件中重复相同的拆卸逻辑。