什么时候我应该存储订阅实例和调用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 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));
}
}
根据@seangwright的回答,我写了一个抽象类来处理组件中“无限”的可观察对象的订阅:
import { OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
import { PartialObserver } from 'rxjs/Observer';
export abstract class InfiniteSubscriberComponent implements OnDestroy {
private onDestroySource: Subject<any> = new Subject();
constructor() {}
subscribe(observable: Observable<any>): Subscription;
subscribe(
observable: Observable<any>,
observer: PartialObserver<any>
): Subscription;
subscribe(
observable: Observable<any>,
next?: (value: any) => void,
error?: (error: any) => void,
complete?: () => void
): Subscription;
subscribe(observable: Observable<any>, ...subscribeArgs): Subscription {
return observable
.takeUntil(this.onDestroySource)
.subscribe(...subscribeArgs);
}
ngOnDestroy() {
this.onDestroySource.next();
this.onDestroySource.complete();
}
}
要使用它,只需在你的angular组件中扩展它,并调用subscribe()方法,如下所示:
this.subscribe(someObservable, data => doSomething());
它还像往常一样接受错误和完整的回调,接受一个观察者对象,或者根本不接受回调。如果你在子组件中也实现了这个方法,记得调用super.ngOnDestroy()。
在这里可以找到Ben Lesh的另一篇参考文章:RxJS: Don’t Unsubscribe。
对于每一个订阅,你都需要取消订阅。Advantage =>以防止状态变得过于沉重。
例如:
在组件1中:
import {UserService} from './user.service';
private user = {name: 'test', id: 1}
constructor(public userService: UserService) {
this.userService.onUserChange.next(this.user);
}
在服务:
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
public onUserChange: BehaviorSubject<any> = new BehaviorSubject({});
于component2:
import {Subscription} from 'rxjs/Subscription';
import {UserService} from './user.service';
private onUserChange: Subscription;
constructor(public userService: UserService) {
this.onUserChange = this.userService.onUserChange.subscribe(user => {
console.log(user);
});
}
public ngOnDestroy(): void {
// note: Here you have to be sure to unsubscribe to the subscribe item!
this.onUserChange.unsubscribe();
}
如果需要取消订阅,可以使用以下可观察管道方法的操作符
import { Observable, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { OnDestroy } from '@angular/core';
export const takeUntilDestroyed = (componentInstance: OnDestroy) => <T>(observable: Observable<T>) => {
const subjectPropertyName = '__takeUntilDestroySubject__';
const originalOnDestroy = componentInstance.ngOnDestroy;
const componentSubject = componentInstance[subjectPropertyName] as Subject<any> || new Subject();
componentInstance.ngOnDestroy = (...args) => {
originalOnDestroy.apply(componentInstance, args);
componentSubject.next(true);
componentSubject.complete();
};
return observable.pipe(takeUntil<T>(componentSubject));
};
它可以这样使用:
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
@Component({ template: '<div></div>' })
export class SomeComponent implements OnInit, OnDestroy {
ngOnInit(): void {
const observable = Observable.create(observer => {
observer.next('Hello');
});
observable
.pipe(takeUntilDestroyed(this))
.subscribe(val => console.log(val));
}
ngOnDestroy(): void {
}
}
操作符包装组件的ngOnDestroy方法。
重点:操作符应该在可观察管道的最后一个。
基于:使用类继承来钩子到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));
}
}
由于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');
}
}