什么时候我应该存储订阅实例和调用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();
}
你可以使用最新的订阅类来取消对Observable的订阅,代码不会那么乱。
我们可以用普通变量来做这个,但它会在每次新的订阅上覆盖上一次订阅,所以要避免这种情况,当你处理更多数量的可观察对象时,这种方法是非常有用的,以及像behaviour Subject和Subject这样的可观察对象类型
订阅
表示一个一次性资源,比如Observable的执行。订阅有一个重要的方法,即unsubscribe,它不接受参数,只处理订阅所持有的资源。
你可以用两种方式来使用它,
you can directly push the subscription to Subscription Array
subscriptions:Subscription[] = [];
ngOnInit(): void {
this.subscription.push(this.dataService.getMessageTracker().subscribe((param: any) => {
//...
}));
this.subscription.push(this.dataService.getFileTracker().subscribe((param: any) => {
//...
}));
}
ngOnDestroy(){
// prevent memory leak when component destroyed
this.subscriptions.forEach(s => s.unsubscribe());
}
using add() of Subscription
subscriptions = new Subscription();
this.subscriptions.add(subscribeOne);
this.subscriptions.add(subscribeTwo);
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
订阅可以保存子订阅并安全地取消所有订阅。这个方法处理可能的错误(例如,如果任何子订阅为空)。
希望这能有所帮助。:)
你不需要有一堆订阅和取消手动订阅。使用Subject和takeUntil组合来像boss一样处理订阅:
import { Subject } from "rxjs"
import { takeUntil } from "rxjs/operators"
@Component({
moduleId: __moduleName,
selector: "my-view",
templateUrl: "../views/view-route.view.html"
})
export class ViewRouteComponent implements OnInit, OnDestroy {
componentDestroyed$: Subject<boolean> = new Subject()
constructor(private titleService: TitleService) {}
ngOnInit() {
this.titleService.emitter1$
.pipe(takeUntil(this.componentDestroyed$))
.subscribe((data: any) => { /* ... do something 1 */ })
this.titleService.emitter2$
.pipe(takeUntil(this.componentDestroyed$))
.subscribe((data: any) => { /* ... do something 2 */ })
//...
this.titleService.emitterN$
.pipe(takeUntil(this.componentDestroyed$))
.subscribe((data: any) => { /* ... do something N */ })
}
ngOnDestroy() {
this.componentDestroyed$.next(true)
this.componentDestroyed$.complete()
}
}
@acumartini在评论中提出了另一种方法,使用takeWhile而不是takeUntil。你可能更喜欢它,但要注意,这样你的Observable的执行就不会在组件的ngDestroy上被取消(例如,当你进行耗时的计算或等待来自服务器的数据时)。方法没有这个缺陷,它会导致立即取消请求。感谢@AlexChe在评论中的详细解释。
代码如下:
@Component({
moduleId: __moduleName,
selector: "my-view",
templateUrl: "../views/view-route.view.html"
})
export class ViewRouteComponent implements OnInit, OnDestroy {
alive: boolean = true
constructor(private titleService: TitleService) {}
ngOnInit() {
this.titleService.emitter1$
.pipe(takeWhile(() => this.alive))
.subscribe((data: any) => { /* ... do something 1 */ })
this.titleService.emitter2$
.pipe(takeWhile(() => this.alive))
.subscribe((data: any) => { /* ... do something 2 */ })
// ...
this.titleService.emitterN$
.pipe(takeWhile(() => this.alive))
.subscribe((data: any) => { /* ... do something N */ })
}
ngOnDestroy() {
this.alive = false
}
}
视情况而定。如果通过调用someObservable.subscribe(),您开始占用一些资源,当组件的生命周期结束时必须手动释放这些资源,那么您应该调用subscription .unsubscribe()来防止内存泄漏。
让我们仔细看看你的例子:
getHero()返回http.get()的结果。如果你查看angular 2的源代码,会发现http.get()创建了两个事件监听器:
_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
通过调用unsubscribe(),你可以取消请求和监听器:
_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();
请注意,_xhr是特定于平台的,但我认为在您的情况下,可以安全地假设它是XMLHttpRequest()。
通常,这已经足够证明需要手动unsubscribe()调用了。但是根据WHATWG规范,XMLHttpRequest()一旦“完成”就会受到垃圾收集的影响,即使有事件监听器附加到它。所以我想这就是为什么angular 2官方指南省略了unsubscribe(),让GC清理侦听器。
至于第二个例子,它取决于参数的实现。从今天起,angular官方指南不再显示取消订阅参数。我再次查看了src,发现params只是一个行为主体。由于没有使用事件侦听器或计时器,也没有创建全局变量,因此省略unsubscribe()应该是安全的。
你的问题的底线是总是调用unsubscribe()来防止内存泄漏,除非你确定可观察对象的执行不会创建全局变量、添加事件侦听器、设置计时器或做任何其他导致内存泄漏的事情。
如果有疑问,请查看该可观察对象的实现。如果可观察对象已经在其unsubscribe()中写入了一些清理逻辑,这通常是构造函数返回的函数,那么你有充分的理由认真考虑调用unsubscribe()。
基于:使用类继承来钩子到Angular 2的组件生命周期
另一种通用方法:
导出抽象类UnsubscribeOnDestroy实现OnDestroy {
protected d$: Subject<any>;
构造函数(){
这一点。d$ = new Subject<void>();
const f = this.ngOnDestroy;
这一点。ngOnDestroy = () => {
f ();
this.d $ . next ();
this.d .complete美元();
};
}
public ngOnDestroy() {
/ /空操作
}
}
并使用:
@ component ({
选择器:“my-comp”,
模板:“
})
导出类RsvpFormSaveComponent扩展UnsubscribeOnDestroy实现OnInit {
构造函数(){
超级();
}
ngOnInit(): void {
Observable.of (bla)
.takeUntil (this.d $)
.subscribe(val => console.log(val));
}
}
Angular 2官方文档提供了一个关于何时退订以及何时可以安全忽略的解释。看看这个链接:
https://angular.io/docs/ts/latest/cookbook/component-communication.html !# bidirectional-service
寻找标题为“父母和孩子通过服务通信”的段落,然后是蓝色方框:
注意,当AstronautComponent被销毁时,我们捕获了订阅并取消了订阅。这是一个内存泄漏保护步骤。在这个应用程序中没有实际的风险,因为AstronautComponent的生命周期与应用程序本身的生命周期相同。在更复杂的应用程序中,这并不总是正确的。
我们没有将这个守卫添加到MissionControlComponent中,因为作为父组件,它控制着MissionService的生命周期。
我希望这对你有所帮助。
更新Angular 9和Rxjs 6解决方案
在Angular组件的ngDestroy生命周期中使用unsubscribe
class SampleComponent implements OnInit, OnDestroy {
private subscriptions: Subscription;
private sampleObservable$: Observable<any>;
constructor () {}
ngOnInit(){
this.subscriptions = this.sampleObservable$.subscribe( ... );
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
}
在Rxjs中使用takeUntil
class SampleComponent implements OnInit, OnDestroy {
private unsubscribe$: new Subject<void>;
private sampleObservable$: Observable<any>;
constructor () {}
ngOnInit(){
this.subscriptions = this.sampleObservable$
.pipe(takeUntil(this.unsubscribe$))
.subscribe( ... );
}
ngOnDestroy() {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
}
你在ngOnInit调用的一些动作,在组件init时只会发生一次。
class SampleComponent implements OnInit {
private sampleObservable$: Observable<any>;
constructor () {}
ngOnInit(){
this.subscriptions = this.sampleObservable$
.pipe(take(1))
.subscribe( ... );
}
}
我们也有async管道。但是,这个是在模板上使用的(不是在Angular组件中)。