Понимание инъекции зависимостей¶
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 может вызвать конструктор компонента с этими сервисами в качестве аргументов.
