你可能知道当你订阅 Observable 对象或设置事件监听时,在某个时间点,你需要执行取消订阅操作,进而释放操作系统的内存。否则,你的应用程序可能会出现内存泄露。
接下来让我们看一下,需要在 ngOnDestroy 生命周期钩子中,手动执行取消订阅操作的一些常见场景。
手动释放资源场景
表单
this.form = new FormGroup({...});
// 监听表单值的变化
this.valueChanges = this.form.valueChanges.subscribe(console.log);
// 监听表单状态的变化
this.statusChanges = this.form.statusChanges.subscribe(console.log);
}
ngOnDestroy() {
this.valueChanges.unsubscribe();
this.statusChanges.unsubscribe();
}
}
以上方案也适用于其它的表单控件。
路由
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 服务
this.click = this.renderer
.listen(this.element.nativeElement,"click",handler);
}
ngOnDestroy() {
this.click.unsubscribe();
}
}
Infinite Observables
当你使用 interval() 或 fromEvent() 操作符时,你创建的是一个无限的 Observable 对象。这样的话,当我们不再需要使用它们的时候,就需要取消订阅,手动释放资源。
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();
}
}
Redux Store
todos: Subscription;
ngOnInit() {
/**
- select(key : string) {
- return this.map(state => state[key]).distinctUntilChanged();
- }
*/
this.todos = this.store.select('todos').subscribe(console.log);
}
ngOnDestroy() {
this.todos.unsubscribe();
}
}
无需手动释放资源场景
AsyncPipe
ngOnInit() {
this.todos$ = this.store.select('todos');
}
}
当组件销毁时,async 管道会自动执行取消订阅操作,进而避免内存泄露的风险。
Angular AsyncPipe 源码片段
if (this._subscription) {
this._dispose();
}
}
}
@HostListener
需要注意的是,如果使用 @HostListener 装饰器,添加事件监听时,我们无法手动取消订阅。如果需要手动移除事件监听的话,可以使用以下的方式:
// unsubscribe
this.handler();
Finite Observable
当你使用 HTTP 服务或 timer Observable 对象时,你也不需要手动执行取消订阅操作。
// 表示1s后发出值,然后就结束了
Observable.timer(1000).subscribe(console.log);
this.http.get('http://api.com').subscribe(console.log);
}
}
操作符签名
操作符作用
timer 返回一个发出无限自增数列的 Observable,具有一定的时间间隔,这个间隔由你来选择。
操作符示例
// 5秒后发出一个数字
var numbers = Rx.Observable.timer(5000);
numbers.subscribe(x => console.log(x));
最终建议
你应该尽可能少的调用 unsubscribe() 方法,你可以在RxJS: Don't Unsubscribe 这篇文章中了解与 Subject 相关更多信息。
具体示例如下:
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();
}
}
操作符签名
操作符作用
发出源 Observable 发出的值,直到 notifier Observable 发出值。
操作符示例