Понимание инъекции зависимостей¶
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 |
|
Следующий шаг — сделать ее доступной в DI, предоставив ее. Зависимость может быть предоставлена в нескольких местах:
- На уровне компонента, используя поле
providers
декоратора@Component
. В этом случаеHeroService
становится доступным для всех экземпляров этого компонента и других компонентов и директив, используемых в шаблоне. Например:
1 2 3 4 5 6 |
|
Когда вы регистрируете провайдера на уровне компонента, вы получаете новый экземпляр сервиса с каждым новым экземпляром этого компонента.
- На уровне
NgModule
, используя полеproviders
декоратора@NgModule
. В этом сценарииHeroService
доступен для всех компонентов, директив и пайпов, объявленных в этомNgModule
или другомNgModule
, который находится внутри того жеModuleInjector
, применимого к этомуNgModule
. Когда вы регистрируете провайдера в определенномNgModule
, один и тот же экземпляр сервиса становится доступным для всех применимых компонентов, директив и пайпов.
Чтобы понять все крайние случаи, смотрите Иерархические инжекторы. Например:
1 2 3 4 5 |
|
- На уровне корня приложения, что позволяет инжектировать его в другие классы приложения. Это можно сделать, добавив поле
providedIn: 'root'
в декоратор@Injectable
:
1 2 3 4 |
|
Когда вы предоставляете сервис на корневом уровне, Angular создает единственный, общий экземпляр HeroService
и внедряет его в любой класс, который его запрашивает. Регистрация провайдера в метаданных @Injectable
также позволяет Angular оптимизировать приложение, удаляя сервис из скомпилированного приложения, если он не используется, процесс, известный как tree-shaking.
Инжектирование зависимости¶
Самый распространенный способ внедрения зависимости — объявить ее в конструкторе класса. Когда Angular создает новый экземпляр компонента, директивы или класса pipe
, он определяет, какие сервисы или другие зависимости нужны этому классу, глядя на типы параметров конструктора. Например, если HeroListComponent
нуждается в HeroService
, конструктор может выглядеть следующим образом:
1 2 3 4 5 6 |
|
Когда Angular обнаруживает, что компонент зависит от сервиса, он сначала проверяет, есть ли в инжекторе существующие экземпляры этого сервиса. Если запрошенный экземпляр сервиса еще не существует, инжектор создает его, используя зарегистрированного провайдера, и добавляет его в инжектор перед тем, как вернуть сервис в Angular.
Когда все запрошенные сервисы будут разрешены и возвращены, Angular может вызвать конструктор компонента с этими сервисами в качестве аргументов.