什么时候我应该存储订阅实例和调用unsubscribe()在ngOnDestroy生命周期,什么时候我可以简单地忽略它们?
保存所有订阅会给组件代码带来很多麻烦。
HTTP客户端指南忽略这样的订阅:
getHeroes() {
this.heroService.getHeroes()
.subscribe(
heroes => this.heroes = heroes,
error => this.errorMessage = <any>error);
}
同时,《航路指南》指出:
最终,我们会航行到别的地方。路由器将从DOM中移除这个组件并销毁它。在那之前,我们得把自己弄干净。具体来说,我们必须在Angular销毁该组件之前取消订阅。如果不这样做,可能会产生内存泄漏。
我们在ngOnDestroy方法中取消订阅我们的可观察对象。
private sub: any;
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
let id = +params['id']; // (+) converts string 'id' to a number
this.service.getHero(id).then(hero => this.hero = hero);
});
}
ngOnDestroy() {
this.sub.unsubscribe();
}
当组件被销毁时,你通常需要取消订阅,但随着我们的发展,Angular会越来越多地处理它,例如在Angular4的新小版本中,他们有这个部分用于路由取消订阅:
你需要取消订阅吗?如在
ActivatedRoute:一站式的路线信息部分
路由和导航页面,路由器管理它的观察对象
提供并本地化订阅。订阅是
当组件被破坏时进行清理,以防止内存
泄漏,所以你不需要从路由paramMap取消订阅
可观察到的。
下面的例子也是Angular中创建一个组件并销毁它的好例子,看看组件是如何实现OnDestroy的,如果你需要onInit,你也可以在你的组件中实现它,比如实现onInit, OnDestroy
import { Component, Input, OnDestroy } from '@angular/core';
import { MissionService } from './mission.service';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector: 'my-astronaut',
template: `
<p>
{{astronaut}}: <strong>{{mission}}</strong>
<button
(click)="confirm()"
[disabled]="!announced || confirmed">
Confirm
</button>
</p>
`
})
export class AstronautComponent implements OnDestroy {
@Input() astronaut: string;
mission = '<no mission announced>';
confirmed = false;
announced = false;
subscription: Subscription;
constructor(private missionService: MissionService) {
this.subscription = missionService.missionAnnounced$.subscribe(
mission => {
this.mission = mission;
this.announced = true;
this.confirmed = false;
});
}
confirm() {
this.confirmed = true;
this.missionService.confirmMission(this.astronaut);
}
ngOnDestroy() {
// prevent memory leak when component destroyed
this.subscription.unsubscribe();
}
}
下面是我对这个问题的看法,保持我的生活简单,我选择手动方式取消订阅时,组件被破坏。
为此,我创建了一个名为Subscriptor的类,它主要包含静态成员,即:
私有变量subscriptions——它保存所有提供的订阅
订阅设置器——将每个新订阅推送到订阅数组
一个取消订阅方法——如果定义了订阅数组,则取消订阅数组中包含的每个订阅,并清空订阅数组
subscriptor.ts
import { Subscription } from "rxjs";
export class Subscriptor {
private static subscriptions: Subscription[] = [];
static set subscription(subscription: Subscription) {
Subscriptor.subscriptions.push(subscription);
}
static unsubscribe() {
Subscriptor.subscriptions.forEach(subscription => subscription ? subscription.unsubscribe() : 0);
Subscriptor.subscriptions = [];
}
}
组件内部的用法如下:
当您想订阅任何服务时,只需将订阅放到Subscriptor的setter中即可。
ngOnInit(): void {
Subscriptor.subscription = this.userService.getAll().subscribe(users => this.users = users);
Subscriptor.subscription = this.categoryService.getAll().subscribe(categories => this.categories = categories);
Subscriptor.subscription = this.postService.getAll().subscribe(posts => this.posts = posts);
}
当您想取消订阅任何服务时,只需调用Subscriptor的unsubscribe方法。
ngOnDestroy(): void {
Subscriptor.unsubscribe();
}
由于seangwright的解决方案(编辑3)似乎非常有用,我也发现将这个功能打包到基本组件中是一个痛苦的过程,并提示其他项目队友记住在ngOnDestroy上调用super()来激活这个功能。
这个答案提供了一种从super调用中释放的方法,并使"componentDestroyed$"成为base component的核心。
class BaseClass {
protected componentDestroyed$: Subject<void> = new Subject<void>();
constructor() {
/// wrap the ngOnDestroy to be an Observable. and set free from calling super() on ngOnDestroy.
let _$ = this.ngOnDestroy;
this.ngOnDestroy = () => {
this.componentDestroyed$.next();
this.componentDestroyed$.complete();
_$();
}
}
/// placeholder of ngOnDestroy. no need to do super() call of extended class.
ngOnDestroy() {}
}
然后你可以自由地使用这个功能,例如:
@Component({
selector: 'my-thing',
templateUrl: './my-thing.component.html'
})
export class MyThingComponent extends BaseClass implements OnInit, OnDestroy {
constructor(
private myThingService: MyThingService,
) { super(); }
ngOnInit() {
this.myThingService.getThings()
.takeUntil(this.componentDestroyed$)
.subscribe(things => console.log(things));
}
/// optional. not a requirement to implement OnDestroy
ngOnDestroy() {
console.log('everything works as intended with or without super call');
}
}