您好,登錄后才能下訂單哦!
你可能知道當你訂閱 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); } }
timer 操作符
操作符簽名
操作符作用
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(); } }
takeUntil 操作符
操作符簽名
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));
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。