浅谈Angular 中何时取消订阅
你可能知道当你订阅 observable 对象或设置事件监听时,在某个时间点,你需要执行取消订阅操作,进而释放操作系统的内存。否则,你的应用程序可能会出现内存泄露。
接下来让我们看一下,需要在 ngondestroy 生命周期钩子中,手动执行取消订阅操作的一些常见场景。
手动释放资源场景
表单
export class testcomponent { ngoninit() { 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(); } }
以上方案也适用于其它的表单控件。
路由
export class testcomponent { constructor(private route: activatedroute, private router: 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 服务
export class testcomponent { constructor( private renderer: renderer2, private element : elementref) { } ngoninit() { this.click = this.renderer .listen(this.element.nativeelement, "click", handler); } ngondestroy() { this.click.unsubscribe(); } }
infinite observables
当你使用 interval() 或 fromevent() 操作符时,你创建的是一个无限的 observable 对象。这样的话,当我们不再需要使用它们的时候,就需要取消订阅,手动释放资源。
export class testcomponent { constructor(private element : 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(); } }
redux store
export class testcomponent { constructor(private store: 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
@component({ selector: 'test', template: `<todos [todos]="todos$ | async"></todos>` }) export class testcomponent { constructor(private store: store) { } ngoninit() { this.todos$ = this.store.select('todos'); } }
当组件销毁时,async 管道会自动执行取消订阅操作,进而避免内存泄露的风险。
angular asyncpipe 源码片段
@pipe({name: 'async', pure: false}) export class asyncpipe implements ondestroy, pipetransform { // ... constructor(private _ref: changedetectorref) {} ngondestroy(): void { if (this._subscription) { this._dispose(); } } }
@hostlistener
export class testdirective { @hostlistener('click') onclick() { .... } }
需要注意的是,如果使用 @hostlistener 装饰器,添加事件监听时,我们无法手动取消订阅。如果需要手动移除事件监听的话,可以使用以下的方式:
// subscribe this.handler = this.renderer.listen('document', "click", event =>{...}); // unsubscribe this.handler();
finite observable
当你使用 http 服务或 timer observable 对象时,你也不需要手动执行取消订阅操作。
export class testcomponent { constructor(private http: http) { } ngoninit() { // 表示1s后发出值,然后就结束了 observable.timer(1000).subscribe(console.log); this.http.get('http://api.com').subscribe(console.log); } }
操作符签名
public static timer(initialdelay: number | date, period: number, scheduler: scheduler): observable
操作符作用
timer 返回一个发出无限自增数列的 observable,具有一定的时间间隔,这个间隔由你来选择。
操作符示例
// 每隔1秒发出自增的数字,3秒后开始发送 var numbers = rx.observable.timer(3000, 1000); numbers.subscribe(x => console.log(x)); // 5秒后发出一个数字 var numbers = rx.observable.timer(5000); numbers.subscribe(x => console.log(x));
最终建议
你应该尽可能少的调用 unsubscribe() 方法,你可以在rxjs: don't unsubscribe 这篇文章中了解与 subject 相关更多信息。
具体示例如下:
export class testcomponent { constructor(private store: store) { } private componetdestroyed: subject = new 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(); } }
操作符签名
public takeuntil(notifier: observable): observable<t>
操作符作用
发出源 observable 发出的值,直到 notifier observable 发出值。
操作符示例
var interval = rx.observable.interval(1000); var clicks = rx.observable.fromevent(document, 'click'); var result = interval.takeuntil(clicks); result.subscribe(x => console.log(x));
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 基于Redis的分布式服务限流方案
下一篇: python 第一课作用