浅谈Angular 中何时取消订阅
你可能知道当你订阅Observable对象或设置事件监听时,在某个时间点,你需要执行取消订阅操作,进而释放操作系统的内存。否则,你的应用程序可能会出现内存泄露。
接下来让我们看一下,需要在ngOnDestroy生命周期钩子中,手动执行取消订阅操作的一些常见场景。
手动释放资源场景
表单
exportclassTestComponent{
ngOnInit(){
this.form=newFormGroup({...});
//监听表单值的变化
this.valueChanges=this.form.valueChanges.subscribe(console.log);
//监听表单状态的变化
this.statusChanges=this.form.statusChanges.subscribe(console.log);
}
ngOnDestroy(){
this.valueChanges.unsubscribe();
this.statusChanges.unsubscribe();
}
}
以上方案也适用于其它的表单控件。
路由
exportclassTestComponent{
constructor(privateroute:ActivatedRoute,privaterouter:Router){}
ngOnInit(){
this.route.params.subscribe(console.log);
this.route.queryParams.subscribe(console.log);
this.route.fragment.subscribe(console.log);
this.route.data.subscribe(console.log);
this.route.url.subscribe(console.log);
this.router.events.subscribe(console.log);
}
ngOnDestroy(){
//手动执行取消订阅的操作
}
}
Renderer服务
exportclassTestComponent{
constructor(
privaterenderer:Renderer2,
privateelement:ElementRef){}
ngOnInit(){
this.click=this.renderer
.listen(this.element.nativeElement,"click",handler);
}
ngOnDestroy(){
this.click.unsubscribe();
}
}
InfiniteObservables
当你使用interval()或fromEvent()操作符时,你创建的是一个无限的Observable对象。这样的话,当我们不再需要使用它们的时候,就需要取消订阅,手动释放资源。
exportclassTestComponent{
constructor(privateelement:ElementRef){}
interval:Subscription;
click:Subscription;
ngOnInit(){
this.interval=Observable.interval(1000).subscribe(console.log);
this.click=Observable.fromEvent(this.element.nativeElement,'click')
.subscribe(console.log);
}
ngOnDestroy(){
this.interval.unsubscribe();
this.click.unsubscribe();
}
}
ReduxStore
exportclassTestComponent{
constructor(privatestore:Store){}
todos:Subscription;
ngOnInit(){
/**
*select(key:string){
*returnthis.map(state=>state[key]).distinctUntilChanged();
*}
*/
this.todos=this.store.select('todos').subscribe(console.log);
}
ngOnDestroy(){
this.todos.unsubscribe();
}
}
无需手动释放资源场景
AsyncPipe
@Component({
selector:'test',
template:` `
})
exportclassTestComponent{
constructor(privatestore:Store){}
ngOnInit(){
this.todos$=this.store.select('todos');
}
}
当组件销毁时,async管道会自动执行取消订阅操作,进而避免内存泄露的风险。
AngularAsyncPipe源码片段
@Pipe({name:'async',pure:false})
exportclassAsyncPipeimplementsOnDestroy,PipeTransform{
//...
constructor(private_ref:ChangeDetectorRef){}
ngOnDestroy():void{
if(this._subscription){
this._dispose();
}
}
}
@HostListener
exportclassTestDirective{
@HostListener('click')
onClick(){
....
}
}
需要注意的是,如果使用@HostListener装饰器,添加事件监听时,我们无法手动取消订阅。如果需要手动移除事件监听的话,可以使用以下的方式:
//subscribe
this.handler=this.renderer.listen('document',"click",event=>{...});
//unsubscribe
this.handler();
FiniteObservable
当你使用HTTP服务或timerObservable对象时,你也不需要手动执行取消订阅操作。
exportclassTestComponent{
constructor(privatehttp:Http){}
ngOnInit(){
//表示1s后发出值,然后就结束了
Observable.timer(1000).subscribe(console.log);
this.http.get('http://api.com').subscribe(console.log);
}
}
timer操作符
操作符签名
publicstatictimer(initialDelay:number|Date,period:number,scheduler:Scheduler):Observable
操作符作用
timer返回一个发出无限自增数列的Observable,具有一定的时间间隔,这个间隔由你来选择。
操作符示例
//每隔1秒发出自增的数字,3秒后开始发送 varnumbers=Rx.Observable.timer(3000,1000); numbers.subscribe(x=>console.log(x)); //5秒后发出一个数字 varnumbers=Rx.Observable.timer(5000); numbers.subscribe(x=>console.log(x));
最终建议
你应该尽可能少的调用unsubscribe()方法,你可以在RxJS:Don'tUnsubscribe这篇文章中了解与Subject相关更多信息。
具体示例如下:
exportclassTestComponent{
constructor(privatestore:Store){}
privatecomponetDestroyed:Subject=newSubject();
todos:Subscription;
posts:Subscription;
ngOnInit(){
this.todos=this.store.select('todos')
.takeUntil(this.componetDestroyed).subscribe(console.log);
this.posts=this.store.select('posts')
.takeUntil(this.componetDestroyed).subscribe(console.log);
}
ngOnDestroy(){
this.componetDestroyed.next();
this.componetDestroyed.unsubscribe();
}
}
takeUntil操作符
操作符签名
publictakeUntil(notifier:Observable):Observable
操作符作用
发出源Observable发出的值,直到notifierObservable发出值。
操作符示例
varinterval=Rx.Observable.interval(1000); varclicks=Rx.Observable.fromEvent(document,'click'); varresult=interval.takeUntil(clicks); result.subscribe(x=>console.log(x));
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。