Полное руководство по использованию автоотписки в компонентах и сервисах Angular
При разработке приложений с помощью Angular обычно используются наблюдаемые объекты для управления потоком данных и обработки асинхронных операций. Хотя наблюдаемые объекты предоставляют мощный механизм для обработки событий и данных, они также могут привести к утечкам памяти, если ими не управлять должным образом.
Один из способов предотвратить утечку памяти в Angular — вручную отписаться от наблюдаемых объектов, когда они больше не нужны. Однако это может быть утомительно и подвержено ошибкам, особенно при работе с несколькими наблюдаемыми в нескольких компонентах и службах.
Чтобы упростить процесс отказа от подписки в компонентах и сервисах Angular, мы можем создать собственный декоратор с именем @AutoUnsubscribe
. Этот декоратор автоматически отменяет все подписки при уничтожении компонента или службы. В этом руководстве мы покажем вам, как создать и использовать декоратор @AutoUnsubscribe, и приведем примеры того, как его можно реализовать.
Создание декоратора @AutoUnsubscribe
Декоратор @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
в компоненте и уходим со страницы, служба уничтожается, а подписка автоматически отменяется.
Заключение
В этой статье мы узнали о @AutoUnsubscribe
decorator, который автоматически отписывается от подписок, чтобы предотвратить утечку памяти в приложениях Angular. Мы увидели, как реализовать декоратор @AutoUnsubscribe
как для компонентов, так и для сервисов, и увидели, как он работает на практике, на примерах.
Используя декоратор @AutoUnsubscribe
, мы можем гарантировать, что наши приложения Angular свободны от утечек памяти, вызванных подписками. Это делает наши приложения более надежными и снижает вероятность сбоев из-за проблем с памятью.
Я надеюсь, что эта статья была полезна для понимания важности отказа от подписки в Angular и того, как использовать декоратор @AutoUnsubscribe
для автоматизации этого процесса.
Спасибо за чтение и продолжайте посещать!