淺談Angular 中何時(shí)取消訂閱
你可能知道當(dāng)你訂閱 Observable 對(duì)象或設(shè)置事件監(jiān)聽(tīng)時(shí),在某個(gè)時(shí)間點(diǎn),你需要執(zhí)行取消訂閱操作,進(jìn)而釋放操作系統(tǒng)的內(nèi)存。否則,你的應(yīng)用程序可能會(huì)出現(xiàn)內(nèi)存泄露。
接下來(lái)讓我們看一下,需要在 ngOnDestroy 生命周期鉤子中,手動(dòng)執(zhí)行取消訂閱操作的一些常見(jiàn)場(chǎng)景。
手動(dòng)釋放資源場(chǎng)景
表單
export class TestComponent {
ngOnInit() {
this.form = new FormGroup({...});
// 監(jiān)聽(tīng)表單值的變化
this.valueChanges = this.form.valueChanges.subscribe(console.log);
// 監(jiān)聽(tīng)表單狀態(tài)的變化
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() {
// 手動(dòng)執(zhí)行取消訂閱的操作
}
}
Renderer 服務(wù)
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
當(dāng)你使用 interval() 或 fromEvent() 操作符時(shí),你創(chuàng)建的是一個(gè)無(wú)限的 Observable 對(duì)象。這樣的話(huà),當(dāng)我們不再需要使用它們的時(shí)候,就需要取消訂閱,手動(dòng)釋放資源。
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();
}
}
無(wú)需手動(dòng)釋放資源場(chǎng)景
AsyncPipe
@Component({
selector: 'test',
template: `<todos [todos]="todos$ | async"></todos>`
})
export class TestComponent {
constructor(private store: Store) { }
ngOnInit() {
this.todos$ = this.store.select('todos');
}
}
當(dāng)組件銷(xiāo)毀時(shí),async 管道會(huì)自動(dòng)執(zhí)行取消訂閱操作,進(jìn)而避免內(nèi)存泄露的風(fēng)險(xiǎn)。
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 裝飾器,添加事件監(jiān)聽(tīng)時(shí),我們無(wú)法手動(dòng)取消訂閱。如果需要手動(dòng)移除事件監(jiān)聽(tīng)的話(huà),可以使用以下的方式:
// subscribe
this.handler = this.renderer.listen('document', "click", event =>{...});
// unsubscribe
this.handler();
Finite Observable
當(dāng)你使用 HTTP 服務(wù)或 timer Observable 對(duì)象時(shí),你也不需要手動(dòng)執(zhí)行取消訂閱操作。
export class TestComponent {
constructor(private http: Http) { }
ngOnInit() {
// 表示1s后發(fā)出值,然后就結(jié)束了
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 返回一個(gè)發(fā)出無(wú)限自增數(shù)列的 Observable,具有一定的時(shí)間間隔,這個(gè)間隔由你來(lái)選擇。
操作符示例
// 每隔1秒發(fā)出自增的數(shù)字,3秒后開(kāi)始發(fā)送 var numbers = Rx.Observable.timer(3000, 1000); numbers.subscribe(x => console.log(x)); // 5秒后發(fā)出一個(gè)數(shù)字 var numbers = Rx.Observable.timer(5000); numbers.subscribe(x => console.log(x));
最終建議
你應(yīng)該盡可能少的調(diào)用 unsubscribe() 方法,你可以在RxJS: Don't Unsubscribe 這篇文章中了解與 Subject 相關(guān)更多信息。
具體示例如下:
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>
操作符作用
發(fā)出源 Observable 發(fā)出的值,直到 notifier Observable 發(fā)出值。
操作符示例
var interval = Rx.Observable.interval(1000); var clicks = Rx.Observable.fromEvent(document, 'click'); var result = interval.takeUntil(clicks); result.subscribe(x => console.log(x));
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
AngularJS中實(shí)現(xiàn)動(dòng)畫(huà)效果的方法
本文主要介紹AngularJS 動(dòng)畫(huà),這里對(duì)動(dòng)畫(huà)的資料詳細(xì)介紹并附有效果圖和代碼實(shí)例,有需要的小伙伴參考下2016-07-07
使用AngularJS處理單選框和復(fù)選框的簡(jiǎn)單方法
這篇文章主要介紹了使用AngularJS處理單選框和復(fù)選框的方法,在AngularJS表單的基礎(chǔ)之上編寫(xiě)起來(lái)非常簡(jiǎn)單,需要的朋友可以參考下2015-06-06
Angular限制input框輸入金額(是小數(shù)的話(huà)只保留兩位小數(shù)點(diǎn))
最近做項(xiàng)目遇到這樣的需求輸入框要求輸入金額,只能輸入數(shù)字,可以是小數(shù),必須保留小數(shù)點(diǎn)后兩位。下面分為兩部分代碼給大家介紹實(shí)現(xiàn)代碼,需要的的朋友參考下吧2017-07-07
AngularJS應(yīng)用開(kāi)發(fā)思維之依賴(lài)注入3
這篇文章主要為大家詳細(xì)介紹了AngularJS應(yīng)用開(kāi)發(fā)思維之依賴(lài)注入第三篇,感興趣的小伙伴們可以參考一下2016-08-08
angularjs請(qǐng)求數(shù)據(jù)的方法示例
這篇文章主要給大家介紹了關(guān)于angularjs請(qǐng)求數(shù)據(jù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用angularjs具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
解決Angular.js中使用Swiper插件不能滑動(dòng)的問(wèn)題
下面小編就為大家分享一篇解決Angular.js中使用Swiper插件不能滑動(dòng)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
Angular 與 Component store實(shí)踐示例
這篇文章主要為大家介紹了Angular 與 Component store實(shí)踐示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
angular源碼學(xué)習(xí)第一篇 setupModuleLoader方法
這篇文章主要介紹了angular源碼學(xué)習(xí)第一篇,setupModuleLoader方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10

