什么时候我应该存储订阅实例和调用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应用中使用Rxjs模式,因为它在离开组件时会自动提供订阅和订阅,而这些订阅将被销毁:
请找到一个如何实现它的例子
app.compoennt.ts:
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { BookService } from './book.service';
import { Book } from './book';
@Component({
selector: 'app-observable',
templateUrl: './observable.component.html'
})
export class AppComponent implements OnInit {
books$: Observable<Book[]>
constructor(private bookService: BookService) { }
ngOnInit(): void {
this.books$ = this.bookService.getBooksWithObservable();
}
}
app.compoennt.html:
<h3>AsyncPipe with Promise using NgFor</h3>
<ul>
<li *ngFor="let book of books$ | async" >
Id: {{book?.id}}, Name: {{book?.name}}
</li>
</ul>
订阅本质上只有一个unsubscribe()函数来释放资源或取消可观察对象的执行。
在Angular中,当组件被销毁时,我们必须从Observable中取消订阅。幸运的是,Angular有一个ngOnDestroy钩子,它会在组件被销毁之前被调用,这使得开发人员可以在这里提供清理人员,以避免挂起订阅、打开门户,以及将来可能会在背后伤害我们的事情
@Component({...})
export class AppComponent implements OnInit, OnDestroy {
subscription: Subscription
ngOnInit () {
var observable = Rx.Observable.interval(1000);
this.subscription = observable.subscribe(x => console.log(x));
}
ngOnDestroy() {
this.subscription.unsubscribe()
}
}
我们添加了ngOnDestroy到我们的appcomponent,并在这个上调用unsubscribe方法。订阅可观察到的
如果有多个订阅:
@Component({...})
export class AppComponent implements OnInit, OnDestroy {
subscription1$: Subscription
subscription2$: Subscription
ngOnInit () {
var observable1$ = Rx.Observable.interval(1000);
var observable2$ = Rx.Observable.interval(400);
this.subscription1$ = observable.subscribe(x => console.log("From interval 1000" x));
this.subscription2$ = observable.subscribe(x => console.log("From interval 400" x));
}
ngOnDestroy() {
this.subscription1$.unsubscribe()
this.subscription2$.unsubscribe()
}
}
出于性能原因,总是建议从你的可观察订阅中取消订阅,以避免内存泄漏,有不同的方法,
顺便说一下,我读了大部分的答案,我没有发现有人在谈论异步管道,推荐在Angular应用中使用Rxjs模式,因为它在离开组件时会自动提供订阅和订阅,而这些订阅将被销毁:
请找到一个如何实现它的例子
app.compoennt.ts:
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { BookService } from './book.service';
import { Book } from './book';
@Component({
selector: 'app-observable',
templateUrl: './observable.component.html'
})
export class AppComponent implements OnInit {
books$: Observable<Book[]>
constructor(private bookService: BookService) { }
ngOnInit(): void {
this.books$ = this.bookService.getBooksWithObservable();
}
}
app.compoennt.html:
<h3>AsyncPipe with Promise using NgFor</h3>
<ul>
<li *ngFor="let book of books$ | async" >
Id: {{book?.id}}, Name: {{book?.name}}
</li>
</ul>
订阅类有一个有趣的特性:
表示一个一次性资源,比如Observable的执行。订阅有一个重要的方法,即unsubscribe,它不接受参数,只处理订阅所持有的资源。
此外,可以通过add()方法将订阅分组在一起,该方法将把一个子订阅附加到当前订阅。当订阅未订阅时,它的所有子(及其孙辈)也将被取消订阅。
您可以创建一个聚合订阅对象,对所有订阅进行分组。
为此,您可以创建一个空的Subscription,并使用它的add()方法向其添加订阅。当组件被销毁时,只需要取消订阅聚合订阅。
@Component({ ... })
export class SmartComponent implements OnInit, OnDestroy {
private subscriptions = new Subscription();
constructor(private heroService: HeroService) {
}
ngOnInit() {
this.subscriptions.add(this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes));
this.subscriptions.add(/* another subscription */);
this.subscriptions.add(/* and another subscription */);
this.subscriptions.add(/* and so on */);
}
ngOnDestroy() {
this.subscriptions.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');
}
}