【譯】Angular自動取消訂閱

Zaynex發表於2018-06-18
  • 原文連結:https://netbasal.com/automagically-unsubscribe-in-angular-4487e9853a88
  • 建立時間:2018-06-18
  • 修改時間:2018-06-20
  • 參與人員:@Zaynex,@我愛吃南瓜

如你所知,當你在 Javascript 中訂閱一個 observable 或者事件時,你通常需 要在特定的時候取消訂閱以釋放記憶體。否則,就會導致記憶體洩漏

A memory leak occurs when a section of memory that is no longer being used is still being occupied needlessly instead of being returned to the OS

當一部分記憶體不再使用但它仍被不必要得佔用而不是返回給作業系統時就會產生記憶體洩漏。

在 Angular 的元件或者指令中,你需要在 ngOnDestroy 的生命週期中 unsubscribe。

比如,如果你有一個元件,它有2個訂閱源。

@Component({ 
selector: 'test', template: `...`,
})export class TestComponent {
one$;
two$;
constructor( private store: Store<
any>
, private element : ElementRef ) {
} ngOnInit() {
this.one$ = store.select("data").subscribe(data =>
// do something);
this.two$ = Observable.interval(1000).subscribe(data =>
// do something);

} ngOnDestroy() {
this.one$.unsubscribe();
this.two$.unsubscribe();

}
}複製程式碼

你需要建立 ngOnDestroy 的方法並且給每個訂閱源取消訂閱。

這很不錯,但是我想要這些取消訂閱的過程自動化。如果我可以建立一個 decorator 類去幫我做這個事情會怎樣呢? 我們假設它會像下面這樣:

@Component({ 
selector: 'test', template: `...`,
})@AutoUnsubscribeexport class TestComponent {
one$;
two$ three;
constructor( private store: Store<
any>
, private element : ElementRef) {
} ngOnInit() {
//...same subscriptions
} // Notice that we don't have the ngOnDestroy method anymore
}
複製程式碼

讓我們建立一個類裝飾器並且給它命名為 AutoUnsubscribe

TypescriptBabel 中, 類裝飾器僅僅是一個接受一個引數的、被裝飾的類的建構函式。(a class decorator is just a function that takes one parameter, the constructor of the decorated class.)

類裝飾器作用於類的 constructor,並且觀察、修改或者替換一個類的定義。

export function AutoUnsubscribe( constructor ) { 
const original = constructor.prototype.ngOnDestroy;
constructor.prototype.ngOnDestroy = function () {
for ( let prop in this ) {
const property = this[ prop ];
if ( property &
&
(typeof property.unsubscribe === "function") ) {
property.unsubscribe();

}
} original &
&
typeof original === "function" &
&
original.apply(this, arguments);

};

}複製程式碼

? 這裡於三個簡單的步驟:

  1. 儲存一個引用指向原來的 ngOnDestory 函式。
  2. 建立你自己的 ngOnDestroy,迴圈遍歷類的屬性並且呼叫 unsubscribe 函式,如果存在的話。
  3. 呼叫原來的 ngOndestroy 函式

? 但是…等等,如果因為某些瘋狂的理由,你仍需要保留訂閱源呢?比如當元件登出的時候你並不想取消訂閱 $two 這個訂閱源。

這種情況下我們需要傳入一個引數給裝飾器(一個陣列用來過濾自動訂閱),所以我們需要使用Decorator Factory

A Decorator Factory is simply a function that returns the expression that will be called by the decorator at runtime.一個裝飾器工廠就是一個函式,它會返回的執行期間被裝飾器呼叫的函式。

export function AutoUnsubscribe(blackList = []) { 
return function(constructor) {
const original = constructor.prototype.ngOndestroy;
constructor.prototype.ngOndestroy = function() {
for(let prop in this) {
const property = this[prop];
if(!blackList.includes(prop)) {
if(property &
&
(typeof property.unsubscribe === 'function')) {
property.unsubscribe();

}
}
} original &
&
typeof original === 'function' &
&
origin.apply(this, arguments);

};

}
}複製程式碼

我們只是針對屬性名做了檢測,如果它不在 blacklist 的名單裡那我們就呼叫 unsubscribe()

現在我們可以像這樣使用 decorator:

@AutoUnsubscribe(["one$", "two$"])class TestComponent { 
...
}複製程式碼

? 現在我們完成了!你可以在這裡找到decorator,如果你有更好的想法,歡迎 pull request。

如果你想要更加宣告式的方式在takeUntil 中使用,你可以檢視我的 tiny class decorator,它有能力去做到:

import TakeUntilDestroy from "angular2-take-until-destroy";
@Component({
selector: 'app-inbox', templateUrl: './inbox.component.html'
})@TakeUntilDestroyexport class InboxComponent {
componentDestroy;
constructor( ) {
const timer$ = Observable.interval(1000) .takeUntil(this.componentDestroy()) .subscribe(val =>
console.log(val)) const timer2$ = Observable.interval(2000) .takeUntil(this.componentDestroy()) .subscribe(val =>
console.log(val))
}
}複製程式碼

不要害怕看原始碼,它就那麼幾行!如果你喜歡這篇文件,檢視我之前的一篇————Make your Code Cleaner with Decorators

總結

你可以利用裝飾器為你的應用新增強大的功能。裝飾器不是一個庫也不是框架,所以要善於創造並利用好他們。你可以探索更多的 decorators,在這裡

來源:https://juejin.im/post/5b27a9c0f265da595b48d0f3?utm_medium=fe&utm_source=weixinqun

相關文章