Angular 4+ ngOnDestroy () в эксплуатации - уничтожить наблюдаемое


104

В приложении angular у нас есть ngOnDestroy()ловушка жизненного цикла для компонента / директивы, и мы используем эту ловушку для отмены подписки на наблюдаемые.

Я хочу очистить / уничтожить наблюдаемые объекты, созданные в @injectable()службе. Я видел несколько сообщений, в которых говорилось, что ngOnDestroy()это также можно использовать в сервисе.

Но является ли это хорошей практикой и единственным способом сделать это и когда он будет вызван? кто-нибудь уточнить пожалуйста.

Ответы:


121

Хук жизненного цикла OnDestroy доступен у провайдеров. Согласно документам:

Ловушка жизненного цикла, которая вызывается при уничтожении директивы, канала или службы.

Вот пример :

@Injectable()
class Service implements OnDestroy {
  ngOnDestroy() {
    console.log('Service destroy')
  }
}

@Component({
  selector: 'foo',
  template: `foo`,
  providers: [Service]
})
export class Foo implements OnDestroy {
  constructor(service: Service) {}

  ngOnDestroy() {
    console.log('foo destroy')
  }
}

@Component({
  selector: 'my-app',
  template: `<foo *ngIf="isFoo"></foo>`,
})
export class App {
  isFoo = true;

  constructor() {
    setTimeout(() => {
        this.isFoo = false;
    }, 1000)
  }
}

Обратите внимание, что в приведенном выше коде Serviceесть экземпляр, принадлежащий Fooкомпоненту, поэтому он может быть уничтожен при Fooуничтожении.

Для провайдеров, которые принадлежат к корневому инжектору, это произойдет при уничтожении приложения, это помогает избежать утечек памяти при множественной загрузке, то есть в тестах.

Когда поставщик из родительского инжектора подписан в дочернем компоненте, он не будет уничтожен при уничтожении компонента, это ответственность компонента за отказ от подписки в компоненте ngOnDestroy(как объясняется в другом ответе).


Нет class Service implements OnDestroy? И что вы думаете, когда это вызывается, если услуга предоставляется на уровне модуля
— Шумаил

1
implements OnDestroyни на что не влияет, но может быть добавлен для полноты картины. Он будет вызываться при уничтожении модуля, например appModule.destroy(). Это может быть полезно для инициализации нескольких приложений.
— Estus Flask

1
необходимо ли отказываться от подписки для каждого компонента, использующего службы?
— Али Аббасзаде

2
Plunker не работал у меня, поэтому вот версия примера для StackBlitz
— compuguru

1
Мне было трудно это понять. Но это обсуждение помогло мне понять разницу между локальными и глобальными службами: stackoverflow.com/questions/50056446/… Я думаю, нужно ли вам «очищать» или нет, зависит от объема вашей службы.
— Жасмин

27

Создайте переменную в своем сервисе

subscriptions: Subscriptions[]=[];

Вставьте каждую подписку в массив как

this.subscriptions.push(...)

Напишите dispose()метод

dispose(){
this.subscriptions.forEach(subscription =>subscription.unsubscribe())

Вызовите этот метод из своего компонента во время ngOnDestroy

ngOnDestroy(){
   this.service.dispose();
 }

Спасибо за ваш ответ. Есть ли у нас идеи, когда будет вызван этот ngOnDestroy? ?
— mperle

да, он говорит, что это вызов очистки до того, как директива или компонент будут уничтожены. но я просто хочу понять, применимо ли это и для обслуживания?
— mperle

Никакие службы не будут очищены при выгрузке модуля
— Аравинд

2
крючки жизненного цикла неприменимы@injectables
— Аравинд

@Aravind Я не уверен, когда они были представлены, но они есть .
— Estus Flask

11

Я предпочитаю, чтобы этот takeUntil(onDestroy$)шаблон был включен операторами конвейера. Мне нравится, что этот шаблон более лаконичен, более понятен и ясно передает намерение убить подписку при выполнении OnDestroyловушки жизненного цикла.

Этот шаблон работает как для служб, так и для компонентов, подписывающихся на внедренные наблюдаемые объекты. Приведенный ниже скелетный код должен дать вам достаточно подробностей для интеграции шаблона в вашу собственную службу. Представьте, что вы импортируете сервис под названием InjectedService...

import { InjectedService } from 'where/it/lives';
import { Injectable, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class MyService implements OnDestroy {

  private onDestroy$ = new Subject<boolean>();

  constructor(
    private injectedService: InjectedService
  ) {
    // Subscribe to service, and automatically unsubscribe upon `ngOnDestroy`
    this.injectedService.observableThing().pipe(
      takeUntil(this.onDestroy$)
    ).subscribe(latestTask => {
      if (latestTask) {
        this.initializeDraftAllocations();
      }
    });
  }

  ngOnDestroy() {
    this.onDestroy$.next(true);
    this.onDestroy$.complete();
  }

Тема того, когда и как отказаться от подписки, подробно рассматривается здесь: Angular / RxJs Когда мне следует отказаться от подписки на подписку


5

Уточняю - вам не нужно уничтожать, Observablesа только сделанные на них подписки.

Кажется, что другие указали, что теперь вы также можете использовать их ngOnDestroyсо службами. Ссылка: https://angular.io/api/core/OnDestroy


1
Не могли бы вы подробнее рассказать об этом
— Аравинд

2

Осторожно при использовании токенов

Пытаясь сделать свое приложение как можно более модульным, я часто использую токены поставщика для предоставления услуги компоненту. Кажется, что их ngOnDestroyметоды НЕ называются :-(

например.

export const PAYMENTPANEL_SERVICE = new InjectionToken<PaymentPanelService>('PAYMENTPANEL_SERVICE');

С разделом провайдера в компоненте:

 {
     provide: PAYMENTPANEL_SERVICE,
     useExisting: ShopPaymentPanelService
 }

Мой метод ShopPaymentPanelServiceНЕ вызывается при удалении ngOnDestroyкомпонента. Я только что выяснил это на собственном горьком опыте!

Обходной путь - предоставить услугу вместе с useExisting.

[
   ShopPaymentPanelService,

   {
       provide: PAYMENTPANEL_SERVICE,
       useExisting: ShopPaymentPanelService
   }
]

Когда я это сделал, он ngOnDisposeбыл вызван, как и ожидалось.

Не уверен, ошибка это или нет, но очень неожиданно.


Отличная подсказка! Мне было интересно, почему это не работает в моем случае (я использовал абстрактный класс-интерфейс в качестве токена для конкретной реализации).
— Андрей Синицон
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.