Перейти к содержанию

Понимание инъекции зависимостей

📅 16.05.2023

Инъекция зависимостей, или DI, является одной из фундаментальных концепций в Angular. DI встроен в фреймворк Angular и позволяет классам с декораторами Angular, такими как Components, Directives, Pipes и Injectables, настраивать необходимые им зависимости.

В системе DI существуют две основные роли: потребитель зависимостей и поставщик зависимостей.

Angular облегчает взаимодействие между потребителями зависимостей и поставщиками зависимостей, используя абстракцию под названием Injector. Когда запрашивается зависимость, инжектор проверяет свой реестр на предмет наличия уже доступного экземпляра. Если нет, создается новый экземпляр и сохраняется в реестре. Angular создает инжектор для всего приложения (также известный как "корневой" инжектор) во время процесса загрузки приложения, а также другие инжекторы по мере необходимости. В большинстве случаев вам не нужно вручную создавать инжекторы, но вы должны знать, что существует слой, соединяющий провайдеров и потребителей.

В этой теме рассматриваются основные сценарии того, как класс может выступать в качестве зависимости. Angular также позволяет использовать в качестве зависимостей функции, объекты, примитивные типы, такие как string или Boolean, или любые другие типы. Для получения дополнительной информации смотрите Dependency providers.

Предоставление зависимостей

Представьте, что есть класс HeroService, который должен выступать в качестве зависимости в компоненте.

Первым шагом будет добавление декоратора @Injectable, чтобы показать, что класс может быть инжектирован.

1
2
@Injectable()
class HeroService {}

Следующий шаг — сделать ее доступной в DI, предоставив ее. Зависимость может быть предоставлена в нескольких местах:

  • На уровне компонента, используя поле providers декоратора @Component. В этом случае HeroService становится доступным для всех экземпляров этого компонента и других компонентов и директив, используемых в шаблоне. Например:
1
2
3
4
5
6
@Component({
    selector: 'hero-list',
    template: '...',
    providers: [HeroService],
})
class HeroListComponent {}

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

  • На уровне NgModule, используя поле providers декоратора @NgModule. В этом сценарии HeroService доступен для всех компонентов, директив и пайпов, объявленных в этом NgModule или другом NgModule, который находится внутри того же ModuleInjector, применимого к этому NgModule. Когда вы регистрируете провайдера в определенном NgModule, один и тот же экземпляр сервиса становится доступным для всех применимых компонентов, директив и пайпов.

Чтобы понять все крайние случаи, смотрите Иерархические инжекторы. Например:

1
2
3
4
5
@NgModule({
    declarations: [HeroListComponent],
    providers: [HeroService],
})
class HeroListModule {}
  • На уровне корня приложения, что позволяет инжектировать его в другие классы приложения. Это можно сделать, добавив поле providedIn: 'root' в декоратор @Injectable:
1
2
3
4
@Injectable({
    providedIn: 'root',
})
class HeroService {}

Когда вы предоставляете сервис на корневом уровне, Angular создает единственный, общий экземпляр HeroService и внедряет его в любой класс, который его запрашивает. Регистрация провайдера в метаданных @Injectable также позволяет Angular оптимизировать приложение, удаляя сервис из скомпилированного приложения, если он не используется, процесс, известный как tree-shaking.

Инжектирование зависимости

Самый распространенный способ внедрения зависимости — объявить ее в конструкторе класса. Когда Angular создает новый экземпляр компонента, директивы или класса pipe, он определяет, какие сервисы или другие зависимости нужны этому классу, глядя на типы параметров конструктора. Например, если HeroListComponent нуждается в HeroService, конструктор может выглядеть следующим образом:

1
2
3
4
5
6
@Component({
    /* ... */
})
class HeroListComponent {
    constructor(private service: HeroService) {}
}

Когда Angular обнаруживает, что компонент зависит от сервиса, он сначала проверяет, есть ли в инжекторе существующие экземпляры этого сервиса. Если запрошенный экземпляр сервиса еще не существует, инжектор создает его, используя зарегистрированного провайдера, и добавляет его в инжектор перед тем, как вернуть сервис в Angular.

Когда все запрошенные сервисы будут разрешены и возвращены, Angular может вызвать конструктор компонента с этими сервисами в качестве аргументов.

Service

Что дальше

Комментарии