4

在 Angular 專案中 RxJS 實現 Unsubscribe 取消訂閱的四種常見方法

 2 years ago
source link: https://blog.miniasp.com/post/2022/03/30/4-ways-to-unsubscribe-in-RxJS-and-Angular
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在 Angular 專案中 RxJS 實現 Unsubscribe 取消訂閱的四種常見方法

RxJS 可以將所有 Observable 物件簡單區分成兩種不同類型的,一種是有限事件數量的 Observable 物件,例如 HttpClient 相關 API 在訂閱之後就只會有一筆資料回來,這種類型的 Observable 在訂閱之後是不需要特別取消訂閱的。另一種則是無限事件數量的 Observable 物件,像是 DOM 的事件訂閱,或是使用 RxJS 的 timer 建立運算子(Creation Operators),或是你在元件中訂閱 Router.events 等等,這些 Observable 都沒有結束的一天,因此需要實作取消訂閱(Unsubscribe),否則就可能會導致記憶體洩漏等問題。這篇文章我將分享四種不同的 RxJS 取消訂閱方法。

在從 Angular 元件注入 ActivatedRoute 的時候,該物件裡面有許多屬性都是 Observable<T> 型別,這些 Observable 在訂閱之後,其實是 無限事件數量 的 Observable,但因為這些屬性都與當前頁面的路由有關,當頁面跳轉到其他路由後,所有 Observable 的資料來源會自動關閉,因此在這些 Observable 在訂閱之後都是不需要特別取消訂閱的,算是個特例!

第一種:傳統的取消訂閱方法 (Subscription)

當你的 Angular 元件中沒有太多 Observable 需要在訂閱之後取消訂閱的話,就可以用這種最傳統的 RxJS 取消訂閱方法。

大致上這個方法包含以下步驟:

  1. 宣告一個 Subscription 屬性

    subscription: Subscription;
    
  2. 訂閱時取得 Subscription 物件

    this.subscription = observable.subscribe(observer);
    
  3. 元件摧毀時對 Subscription 物件取消訂閱

    ngOnDestroy(): void {
      this.subscription.unsubscribe();
    }
    

範例程式: Angular + RxJS: Unsubscribe Method 1

第二種:現代的取消訂閱方法 (Subject + takeUntil)

當你一個 Angular 元件中有許多 Observable 需要在訂閱之後取消訂閱的話,就可以考慮用 Subject + takeUntil 的應用技巧批次將所有 Subscription 取消訂閱。

大致上這個方法包含以下步驟:

  1. 宣告一個 Subject 屬性

    destroy$ = new Subject();
    
  2. 訂閱時搭配 .pipe()takeUntil(this.destroy$)ObservableSubject 進行連動

    observable.pipe(takeUntil(this.destroy$)).subscribe(observer);
    
  3. 元件摧毀時對 Subject 發送一個串流資料並設定完成

    ngOnDestroy(): void {
      this.destroy$.next(null);
      this.destroy$.complete();
    }
    

範例程式: Angular + RxJS: Unsubscribe Method 2

第三種:進階的取消訂閱方法 (UntilDestroy)

當你很多 Angular 元件中有許多 Observable 需要在訂閱之後取消訂閱,若不想在每個元件加入 ngOnDestroy() 方法的話,那可以考慮使用 @ngneat/until-destroy 套件,此套件使用到自訂的 Decorator 方法,自動替你的訂閱物件加入「取消訂閱」功能。

大致上這個方法包含以下步驟:

  1. 直接在 @Component() 之上外加一個 @UntilDestroy() 裝飾器 (Decorator)

    @UntilDestroy()
    @Component({
      selector: 'hello',
      template: `<h1>Hello {{counter}}!</h1>`,
      styles: [`h1 { font-family: Lato; }`]
    })
    export class HelloComponent implements OnInit { ... }
    
  2. 訂閱時搭配 .pipe(untilDestroyed(this)) 就可以讓元件摧毀時自動取消訂閱(不用額外儲存 Subscription 物件)

    observable.pipe(untilDestroyed(this)).subscribe(observer);
    

範例程式: Angular + RxJS: Unsubscribe Method 3-1

這個套件還有另外一種神奇用法,一樣可以不用替每個元件加入 ngOnDestroy() 方法,而且你只要在元件類別中宣告 Subscription 型別的屬性即可在元件摧毀時自動取消訂閱。

大致上這個方法包含以下步驟:

  1. 直接在 @Component() 之上外加一個 @UntilDestroy({ checkProperties: true }) 裝飾器 (Decorator)

    @UntilDestroy({ checkProperties: true })
    @Component({
      selector: 'hello',
      template: `<h1>Hello {{counter}}!</h1>`,
      styles: [`h1 { font-family: Lato; }`]
    })
    export class HelloComponent implements OnInit { ... }
    
  2. 宣告一個 Subscription 屬性

    subscription: Subscription;
    
  3. 只要將 Subscription 物件存起來即可(不用額外使用 .pipe(untilDestroyed(this)) 運算子)

    this.subscription = observable.subscribe(observer);
    

範例程式: Angular + RxJS: Unsubscribe Method 3-2

第四種:直接使用 AsyncPipe 訂閱任何 Observable 物件

若你的 Observable 資料最終需要顯示在畫面上,你或許可以認真考慮直接從 Template 使用 AsyncPipe 訂閱 Observable 物件,因為所有使用 AsyncPipe 訂閱的物件,最終都會在元件摧毀時自動取消訂閱,非常方便!

大致上這個方法包含以下步驟:

  1. 宣告一個 Observable 屬性

    counter$!: Observable<number>;
    
  2. 準備好必要的 Observable 物件,必要時可加入 .pipe() 運算子

    let observable = interval(1000);
    
    this.counter$ = observable.pipe(
      tap((i) => console.log('counter = ', i)),
      startWith(0),
    );
    
  3. 直接在 Template 透過 AsyncPipe 訂閱 Observable 物件(完全不用擔心何時要取消訂閱)

    <h1>Hello {{counter$ | async}}!</h1>
    

    如果寫在 *ngIf 上,可以利用 as 取得一個區域變數:

    <h1 *ngIf="counter$ | async as num">Hello {{num}}!</h1>
    

範例程式: Angular + RxJS: Unsubscribe Method 4-1

範例程式: Angular + RxJS: Unsubscribe Method 4-2


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK