Пълното ръководство за използване на автоматично отписване в компоненти и услуги на Angular

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

Един от начините за предотвратяване на изтичане на памет в Angular е ръчно отписване от наблюдаеми, когато вече не са необходими. Това обаче може да бъде досадно и податливо на грешки, особено когато се работи с множество наблюдаеми в множество компоненти и услуги.

За да опростим процеса на отписване от абонаменти в компоненти и услуги на Angular, можем да създадем персонализиран декоратор, наречен @AutoUnsubscribe. Този декоратор автоматично отписва всички абонаменти, когато компонент или услуга бъдат унищожени. В това ръководство ще ви покажем как да създадете и използвате декоратора @AutoUnsubscribe и ще предоставим примери за това как може да бъде приложен.

Създаване на @AutoUnsubscribe Decorator

Декораторът @AutoUnsubscribe е персонализиран декоратор, който можем да създадем сами в нашето Angular приложение. Той работи чрез автоматично отписване от всички абонаменти в компонент или услуга, когато този компонент или услуга бъдат унищожени.

За да създадем декоратора @AutoUnsubscribe, първо трябва да импортираме интерфейса OnDestroy от пакета @angular/core. Този интерфейс съдържа един метод, ngOnDestroy(), който се извиква, когато компонент или услуга бъде унищожена

import { OnDestroy } from '@angular/core';

export function AutoUnsubscribe(constructor: any) {
  // Get a reference to the original ngOnDestroy function of the component/service
  const original = constructor.prototype.ngOnDestroy;

  // Override the ngOnDestroy function of the component/service
  constructor.prototype.ngOnDestroy = function() {
    // Loop through all properties of the component/service
    for (const prop in this) {
      const property = this[prop];
      // Check if the property is a subscription
      if (property && typeof property.unsubscribe === 'function') {
         // Call the unsubscribe function to unsubscribe from the subscription
        property.unsubscribe();
      }
    }

    original?.apply(this, arguments);
  };
}

В този кодов фрагмент ние дефинираме функция, наречена AutoUnsubscribe, която приема конструктор като свой параметър. Конструкторът представлява компонента или услугата, която искаме да украсим.

Във функцията първо съхраняваме препратка към оригиналния ngOnDestroy() метод на прототипа на конструктора. Това е важно, защото искаме да запазим оригиналното поведение на ngOnDestroy(), докато добавяме собствена функционалност.

След това предефинираме метода ngOnDestroy() на прототипа на конструктора. Нашият нов метод ngOnDestroy() преминава през всички свойства на компонента или услугата и проверява дали всяко свойство е абонамент. Ако дадено свойство е абонамент, ние извикваме неговия unsubscribe() метод, за да прекратим абонамента за него.

Накрая извикваме оригиналния метод ngOnDestroy(), ако съществува, за да гарантираме, че всяка допълнителна логика за почистване, дефинирана в компонента или услугата, се изпълнява.

С този код вече можем да използваме декоратора @AutoUnsubscribe за автоматично отписване от абонаменти в нашите Angular компоненти и услуги.

В следващия раздел ще ви покажем как да използвате декоратора @AutoUnsubscribe, като предоставим примери за изпълнение.

Как работи декораторът за автоматично отписване

Декораторът @AutoUnsubscribe е декоратор на TypeScript, който предоставя лесен начин за управление на наблюдаеми в Angular. Когато се прилага към компонент или услуга, декораторът @AutoUnsubscribe автоматично се отписва от всички наблюдавани абонаменти, когато компонентът или услугата бъдат унищожени.

За да използвате декоратора @AutoUnsubscribe, просто го добавете като декоратор към вашия компонент или сервизен клас, като това:

import { AutoUnsubscribe } from './auto-unsubscribe.decorator';

@Component({
  selector: 'app-counter',
  templateUrl: './app-counter.component.html'
})
@AutoUnsubscribe()
export class CounterComponent implements OnInit {
  // ...
}

В този пример добавихме @AutoUnsubscribe декоратора към CounterComponentклас. Сега всички видими абонаменти, създадени в рамките на компонента, ще бъдат автоматично отписани, когато компонентът бъде унищожен.

Под капака декораторът @AutoUnsubscribe работи чрез внедряване на интерфейса на куката за жизнения цикъл OnDestroy. Когато компонентът или услугата бъдат унищожени, Angular извиква метода ngOnDestroy, който от своя страна отписва всички наблюдавани абонаменти.

Примери за изпълнение

Нека да разгледаме някои примери за внедряване, за да видим как декораторът @AutoUnsubscribe работи на практика.

Пример с компонент

Да предположим, че имаме компонент, който се абонира за наблюдаем и актуализира шаблон за изглед с данните:

import { Component, OnInit } from '@angular/core';
import { Subscription, interval } from 'rxjs';

@Component({
  selector: 'app-counter',
  template: `<h1>Count: {{ count }}</h1>`
})
export class CounterComponent implements OnInit {
  private subscription: Subscription;
  public count: number = 0;

  ngOnInit() {
    this.subscription = interval(1000).subscribe(() => {
      this.count++;
    });
  }

  ngOnDestroy() {
    console.log('CounterComponent destroyed');
  }
}

В този пример използваме оператора interval от RxJS, за да излъчваме число всяка секунда. Ние актуализираме свойството count на компонента с това число и използваме интерполация на низове, за да покажем стойността count в изгледа.

Сега да предположим, че навигираме далеч от страницата, преди интервалът да приключи. Без декоратора @AutoUnsubscribe абонаментът ще продължи да излъчва стойности и свойството count ще продължи да се увеличава за неопределено време. Това може да доведе до изтичане на памет и проблеми с производителността.

С декоратора @AutoUnsubscribe обаче, абонаментът се отписва автоматично, когато компонентът бъде унищожен. Можем да проверим това, като добавим лог оператор към метода ngOnDestroy:

ngOnDestroy() {
  console.log('CounterComponent destroyed');
}

Сега, когато навигираме далеч от страницата, отчетът за журнал се отпечатва на конзолата, което показва, че компонентът е унищожен и абонаментът е прекратен.

За да използваме декоратора @AutoUnsubscribe с този компонент, просто го добавяме като декоратор към класа:

import { Component, OnInit } from '@angular/core';
import { Subscription, interval } from 'rxjs';
import { AutoUnsubscribe } from './auto-unsubscribe.decorator';

@Component({
  selector: 'app-counter',
  template: `
    <h1>Count: {{ count }}</h1>
  `
})
@AutoUnsubscribe()
export class CounterComponent implements OnInit {
  private subscription: Subscription;
  public count: number = 0;

  ngOnInit() {
    this.subscription = interval(1000).subscribe(() => {
      this.count++;
    });
  }

  // only for check
  ngOnDestroy() {
    console.log('CounterComponent destroyed');
  }
}

Сега, когато се отдалечим от страницата, CounterComponent се унищожава и абонаментът автоматично се отписва.

Пример с услуга

Да предположим, че имаме услуга, която извлича данни от API и ги предоставя на компонент:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AppService{
  private apiUrl = 'https://jsonplaceholder.typicode.com/posts';

  constructor(private http: HttpClient) {}

  getPosts(): Observable<any[]> {
    return this.http.get<any[]>(this.apiUrl);
  }

  ngOnDestroy() {
    console.log('AppService destroyed');
  }
}

В този пример използваме HttpClient за извличане на данни от API и връщаме наблюдаема, която излъчва масив от публикации. Също така добавихме метод ngOnDestroy към услугата, за да демонстрираме, че се извиква, когато услугата бъде унищожена.

Сега да предположим, че използваме тази услуга в компонент за извличане и показване на публикациите:

import { Component, OnInit } from '@angular/core';
import { AppService} from './app.service';

@Component({
  selector: 'app-counter',
  template: `
    <ul>
      <li *ngFor="let post of posts">
        {{ post.title }}
      </li>
    </ul>
  `
})
export class CounterComponent implements OnInit {
  public posts: any[];

  constructor(private appService: AppService) {}

  ngOnInit() {
    this.appService.getPosts().subscribe(posts => {
      this.posts = posts;
    });
  }
}

В този пример ние инжектираме AppService в CounterComponent конструктора и използваме метода getPosts, за да извлечем публикациите и да ги съхраним в свойството posts на компонента.

Отново, без декоратора @AutoUnsubscribe, абонаментът за метода getPosts ще продължи да излъчва стойности дори след като компонентът бъде унищожен. С декоратора @AutoUnsubscribe обаче, абонаментът се отписва автоматично, когато компонентът бъде унищожен.

За да използваме декоратора @AutoUnsubscribe с тази услуга, просто го добавяме като декоратор към класа:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AutoUnsubscribe } from './auto-unsubscribe.decorator';

@Injectable({
  providedIn: 'root'
})
@AutoUnsubscribe()
export class AppService {
  private apiUrl = 'https://jsonplaceholder.typicode.com/posts';

  constructor(private http: HttpClient) {}

  getPosts(): Observable<any[]> {
    return this.http.get<any[]>(this.apiUrl);
  }

// only for check
ngOnDestroy() {
  console.log('AppService destroyed');
  }
}

Сега, когато използваме AppService в компонент и се отдалечим от страницата, услугата се унищожава и абонаментът автоматично се отписва.

Заключение

В тази статия научихме за @AutoUnsubscribedecorator, който автоматично се отписва от абонаменти, за да предотврати изтичане на памет в Angular приложения. Видяхме как да внедрим декоратора @AutoUnsubscribe както за компоненти, така и за услуги, и видяхме как работи на практика с примери.

С помощта на декоратора @AutoUnsubscribe можем да гарантираме, че нашите Angular приложения нямат течове на памет, причинени от абонаменти. Това прави нашите приложения по-стабилни и по-малко вероятно да се сринат поради проблеми с паметта.

Надявам се, че тази статия е била полезна за разбирането на важността на отписването от абонаменти в Angular и как да използвате декоратора @AutoUnsubscribe за автоматизиране на процеса.

Благодарим ви, че прочетохте и продължавайте да посещавате!