什么时候我应该存储订阅实例和调用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();
}
下面是我对这个问题的看法,保持我的生活简单,我选择手动方式取消订阅时,组件被破坏。
为此,我创建了一个名为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();
}
下面是我对这个问题的看法,保持我的生活简单,我选择手动方式取消订阅时,组件被破坏。
为此,我创建了一个名为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();
}
在我的情况下,我使用了@seanwright提出的解决方案的变化:
https://github.com/NetanelBasal/ngx-take-until-destroy
这是ngx-rocket / starter-kit项目中使用的文件。你可以在这里访问until-destroyed.ts
组件看起来是这样的
/**
* RxJS operator that unsubscribe from observables on destory.
* Code forked from https://github.com/NetanelBasal/ngx-take-until-destroy
*
* IMPORTANT: Add the `untilDestroyed` operator as the last one to
* prevent leaks with intermediate observables in the
* operator chain.
*
* @param instance The parent Angular component or object instance.
* @param destroyMethodName The method to hook on (default: 'ngOnDestroy').
*/
import { untilDestroyed } from '../../core/until-destroyed';
@Component({
selector: 'app-example',
templateUrl: './example.component.html'
})
export class ExampleComponent implements OnInit, OnDestroy {
ngOnInit() {
interval(1000)
.pipe(untilDestroyed(this))
.subscribe(val => console.log(val));
// ...
}
// This method must be present, even if empty.
ngOnDestroy() {
// To protect you, an error will be thrown if it doesn't exist.
}
}
你可以使用最新的订阅类来取消对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();
}
订阅可以保存子订阅并安全地取消所有订阅。这个方法处理可能的错误(例如,如果任何子订阅为空)。
希望这能有所帮助。:)
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组件中)。