Иерархия инжекторов в Angular
Что такое иерархия инжекторов в Angular?
В Angular внедрение зависимостей (Dependency Injection, DI) построено на иерархии инжекторов, где зависимости могут быть доступны:
- Глобально
- Только внутри модуля
- Только в компоненте
- Только в конкретной директиве или pipe
Angular создает дерево инжекторов, схожее с деревом компонентов. Каждый компонент может иметь собственный инжектор, унаследованный от родителя.
Уровни иерархии инжекторов
Глобальный уровень — @Injectable({ providedIn: 'root' })
- Сервис создаётся один раз на всё приложение.
- Доступен везде без необходимости указывать в
providers
.
@Injectable({ providedIn: 'root' })
export class LoggerService {}
Используется по умолчанию для singleton-сервисов.
Модульный уровень (в @NgModule.providers)
- Сервис доступен только в пределах указанного модуля.
- Если модуль импортируется в другие модули — поведение может отличаться.
@NgModule({
providers: [AuthService]
})
export class AuthModule {}
Хорошо для ограниченного доступа и lazy-loaded модулей.
Компонентный уровень (в @Component.providers)
- Сервис создаётся отдельно для каждого экземпляра компонента.
- Подходит для локального состояния, независимого от других компонентов.
@Component({
selector: 'app-cart',
templateUrl: './cart.component.html',
providers: [CartService]
})
export class CartComponent {}
При каждом использовании компонента будет создан новый экземпляр сервиса.
Уровень директив/пайпов (@Directive.providers)
- Аналогично компонентам — сервис создаётся локально, внутри элемента, к которому применяется директива.
@Directive({
selector: '[highlight]',
providers: [HighlightService]
})
export class HighlightDirective {}
Как Angular ищет зависимости
Когда Angular внедряет зависимость, он:
- Сначала смотрит в локальном инжекторе компонента.
- Если не находит — ищет в родительском инжекторе.
- И так до корневого инжектора (
root
). - Если зависимость не найдена — выбрасывает ошибку.
Пример: вложенные компоненты и разные инжекторы
@Component({
selector: 'parent',
providers: [SharedService],
template: `<child></child>`
})
export class ParentComponent {}
@Component({
selector: 'child',
template: `...`
})
export class ChildComponent {
constructor(shared: SharedService) {} // получит instance от parent
}
Если SharedService
не указан в child
, он будет унаследован от родителя.