Tìm kiếm trang web

javascript - Sử dụng một component cơ sở để hủy đăng ký tất cả các subscription observable


Trong một ứng dụng Angular, chúng tôi đang sử dụng một Base Component để hủy đăng ký hầu hết các đăng ký observable của ứng dụng. Nếu một component đăng ký một observable, component đó sẽ extend Base Component. Ý nghĩ của tôi là điều này giữ cho các đăng ký observable tồn tại cho đến khi toàn bộ ứng dụng bị destroyed, thay vì cho đến khi từng component bị hủy:

base.component.ts: :

import { Subject } from 'rxjs';
import { OnDestroy, Component } from '@angular/core';

export abstract class BaseComponent implements OnDestroy {
  protected unsubscribe$ = new Subject<void>();

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

the-rest-of-our-components.ts: :

import { Component, OnInit } from '@angular/core';
import { MyService } from 'src/app/services/my.service';
import { BaseComponent } from '../base/component/base-component';

export class myComponent extends BaseComponent implements OnInit {
  myProperty: string;

  constructor(
    private myService: MyService,
  ) {
    super();
  }
  ngOnInit(): void {
    this.myService.doStuff$
      .pipe(takeUntil(this.unsubscribe$)) // take until baseComponent's unsubscribe$
      .subscribe((data) => {
        this.myProperty = data;
      });
  }

Nếu nhiều thành phần mở rộng BaseComponent và sử dụng Subject unsubscribe$ của nó, có nghĩa là tất cả các đăng ký của tôi chỉ bị hủy khi toàn bộ ứng dụng bị hủy (tức là người dùng đóng tab hoặc rời khỏi web app, do đó Base Component bị hủy), thay vì khi các thành phần riêng lẻ bị hủy không?

Đây có phải là một chiến lược mà bạn từng thấy trước đây không, và liệu nó có được khuyến nghị không? Nếu nó hoạt động như tôi đang giả định, điều này có nghĩa là tất cả các đăng ký của chúng ta trên ứng dụng sẽ vẫn hoạt động cho đến khi toàn bộ ứng dụng bị phá hủy. Tôi thấy rằng, tùy thuộc vào nhu cầu của chúng ta, điều đó có thể là điều xấu hoặc điều tốt.

Câu hỏi thưởng: liệu Base Component có hoạt động giống như một singleton không? Tức là nếu nhiều component cùng lúc extend BaseComponent, liệu tất cả chúng có sử dụng cùng một instance của unsubscribe$ hay sẽ có nhiều instance của unsubscribe$ (mỗi component một instance)?

Câu trả lời #1

Tôi đã giả định điều này sẽ hoạt động, nhưng tất cả chúng ta đều biết giả định dẫn bạn đến đâu, vì vậy tôi đã làm một bài kiểm tra: https://stackblitz.com/edit/angular-ivy-ueshwz?file=src/app/extended/extended.component.ts

Nó hoạt động, nghĩa là các đăng ký bị hủy khi các thành phần riêng lẻ bị hủy.


Chúng tôi tạo ra một dịch vụ giữ một đối tượng mà chúng tôi có thể đăng ký, và một giá trị mà chúng tôi có thể thay đổi cùng với việc đăng ký, để hiển thị rằng việc đăng ký tồn tại:

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/internal/Subject';

@Injectable({ providedIn: 'root' })
export class UpdateService {
  subject = new Subject<void>();
  value = 0;
}

Ở phần gốc, chúng ta sẽ phát chủ đề mỗi giây, và có một thành phần mà chúng ta có thể bật hoặc tắt

export class AppComponent implements OnInit {
  extCompOpen = true;

  constructor(public update: UpdateService) {}

  ngOnInit() {
    interval(1000).subscribe(() => this.update.subject.next());
  }
}
<app-extended *ngIf="extCompOpen"></app-extended>
<button (click)="extCompOpen = !extCompOpen">Toggle Component</button>

<p>This counter will keep going up as long as the subscription exists:</p>
<p>{{ update.value }}</p>

Sau đó chúng ta sẽ sử dụng một thành phần mở rộng để tăng giá trị đó lên 1 thông qua một đăng ký

export class ExtendedComponent extends BaseComponent implements OnInit {
  constructor(private update: UpdateService) {
    super();
  }

  ngOnInit() {
    this.update.subject.pipe(takeUntil(this.unsubscribe$)).subscribe(() => {
      this.update.value++;
    });
  }
}
<p>Extended component exists!</p>

Thú vị, việc đóng thành phần làm dừng bộ đếm thời gian, vì vậy việc đăng ký đã bị hủy.


Câu hỏi thêm: BaseComponent không hoạt động như một singleton, khi bạn tạo một phiên bản của một đối tượng, nó không tạo các phiên bản dùng chung của các lớp cha. Việc kế thừa một lớp chỉ thêm các thuộc tính và phương thức vào phiên bản đó.


Tôi không chắc mình có nên khuyên điều này hay không, nếu ai đó ghi đè ngOnDestroy(), họ cần phải gọi super.ngOnDestroy(), điều này có thể dễ bị quên. Chỉ có bốn dòng mã, có lẽ tốt hơn là nên đặt rõ ràng trong mỗi component cần nó. Việc đăng ký thủ công cũng nên rất hiếm, nếu bạn đang sử dụng async pipe.

Câu trả lời #2

Tôi đã giải quyết việc này trong một dự án bằng cách thực hiện những điều sau:

Trong base.component:

private sub: any = {};

ngOnDestroy() {
  Object.keys(this.sub).map(item => {
     this.sub[item].unsubscribe();
  })
}

Sau đó trong bất kỳ thành phần nào mở rộng:

this.sub.myService = this.myService.doStuff$.subscribe(......

Với phương pháp này, gói đăng ký sẽ vẫn hoạt động cho đến khi thành phần bị hủy.