285 lines
18 KiB
Markdown
285 lines
18 KiB
Markdown
|
---
|
|||
|
title: Dependency Injection
|
|||
|
localeTitle: Внедрение зависимости
|
|||
|
---
|
|||
|
# Внедрение зависимости
|
|||
|
|
|||
|
#### мотивация
|
|||
|
|
|||
|
Инъекция зависимостей часто более просто называется ДИ. Парадигма существует по всему Угловому. Он сохраняет код гибким, проверяемым и изменяемым. Классы могут наследовать внешнюю логику, не зная, как ее создать. Любые потребители этих классов также не должны знать ничего.
|
|||
|
|
|||
|
DI спасает классы и потребителей от необходимости знать больше, чем необходимо. Тем не менее, код такой же модульный, как и раньше, благодаря механизмам поддержки DI в Angular.
|
|||
|
|
|||
|
Услуги являются ключевым благодетелем ДИ. Они полагаются на парадигму для _инъекций_ различным потребителям. Эти потребители могут воспользоваться этим сервисом и / или перенаправить его в другое место.
|
|||
|
|
|||
|
Служба не одинока. Директивы, трубы, компоненты и т. Д.: Каждая схема в Угловой выгоде от DI так или иначе.
|
|||
|
|
|||
|
#### Форсунки
|
|||
|
|
|||
|
Инъекторы представляют собой структуры данных, в которых хранятся инструкции, детализирующие, где и как формируются службы. Они действуют как посредники в системе Angular DI.
|
|||
|
|
|||
|
Классы модулей, директив и компонентов содержат метаданные, специфичные для инжекторов. Новый экземпляр инжектора сопровождает каждый из этих классов. Таким образом, дерево приложений отражает свою иерархию инжекторов.
|
|||
|
|
|||
|
`providers: []` метаданные принимают службы, которые затем регистрируются в инжекторе класса. Это поле поставщика добавляет инструкции, необходимые для работы инжектора. Класс (при условии, что он имеет зависимости) создает экземпляр службы, беря на себя класс в качестве своего типа данных. Инжектор выравнивает этот тип. A создает экземпляр этой службы для имени класса.
|
|||
|
|
|||
|
Разумеется, класс может только указать, для чего инжектор имеет инструкции. Если собственный инжектор класса не имеет зарегистрированной службы, он запрашивает родителя. Так далее и так далее, пока не дойдете до инжектора с помощью сервиса или корня приложения.
|
|||
|
|
|||
|
Услуги могут регистрироваться у любого инжектора в приложении. Услуги входят в `providers: []` поле метаданных модулей модулей, директив или компонентов. Дети класса могут создавать экземпляр службы, зарегистрированной в инжекторе класса. В конце концов, инъекции инъецированных детей на родительские инжекторы.
|
|||
|
|
|||
|
#### Внедрение зависимости
|
|||
|
|
|||
|
Взгляните на скелеты для каждого класса: сервис, модуль, директива и компонент.
|
|||
|
|
|||
|
```typescript
|
|||
|
// service
|
|||
|
|
|||
|
import { Injectable } from '@angular/core';
|
|||
|
|
|||
|
@Injectable({
|
|||
|
providedIn: /* injector goes here */
|
|||
|
})
|
|||
|
export class TemplateService {
|
|||
|
constructor() { }
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
```typescript
|
|||
|
// module
|
|||
|
|
|||
|
import { NgModule } from '@angular/core';
|
|||
|
import { CommonModule } from '@angular/common';
|
|||
|
|
|||
|
@NgModule({
|
|||
|
imports: [
|
|||
|
CommonModule
|
|||
|
],
|
|||
|
declarations: [],
|
|||
|
providers: [ /* services go here */ ]
|
|||
|
})
|
|||
|
export class TemplateModule { }
|
|||
|
```
|
|||
|
|
|||
|
```typescript
|
|||
|
// directive
|
|||
|
|
|||
|
import { Directive } from '@angular/core';
|
|||
|
|
|||
|
@Directive({
|
|||
|
selector: '[appTemplate]',
|
|||
|
providers: [ /* services go here */ ]
|
|||
|
})
|
|||
|
export class TemplateDirective {
|
|||
|
constructor() { }
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
```typescript
|
|||
|
//component
|
|||
|
|
|||
|
import { Component } from '@angular/core';
|
|||
|
|
|||
|
@Component({
|
|||
|
selector: 'app-template',
|
|||
|
templateUrl: './template.component.html',
|
|||
|
styleUrls: ['./template.component.css'],
|
|||
|
providers: [ /* services go here */ ]
|
|||
|
})
|
|||
|
export class TemplateComponent {
|
|||
|
// class logic ...
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
Каждый скелет может регистрировать услуги инжектора. Фактически, TemplateService _\-_ это сервис. Начиная с Angular 6, службы теперь могут регистрироваться в инжекторах с использованием метаданных `@Injectable` .
|
|||
|
|
|||
|
##### В любом случае
|
|||
|
|
|||
|
Обратите внимание на `providedIn: string` ( `@Injectable` ) и `providers: []` ( `@Directive` , `@Componet` и `@Module` ) метаданные. Они сообщают инжекторам, где и как создать сервис. В противном случае инжектора не знали, как создавать экземпляр.
|
|||
|
|
|||
|
Что делать, если служба имеет зависимости? Куда будут идти результаты? Поставщики отвечают на этот вопрос, так что инжектора могут корректно создавать экземпляры.
|
|||
|
|
|||
|
Инъекторы образуют основу каркаса DI. Они хранят инструкции для создания служб, которые не нужны потребителям. Они получают экземпляры службы без необходимости знать что-либо об исходной зависимости!
|
|||
|
|
|||
|
Следует также отметить, что другие схемы без инжекторов все еще могут использовать инъекцию зависимостей. Они не могут регистрировать дополнительные услуги, но они все еще могут быть созданы из инжекторов.
|
|||
|
|
|||
|
##### обслуживание
|
|||
|
|
|||
|
`providedIn: string` метаданные `@Injectable` указывают, какой инжектор зарегистрировать. Используя этот метод, и в зависимости от того, будет ли использоваться услуга, служба может или не может зарегистрироваться в инжекторе. Угловые называет это _дрожание дерева_ .
|
|||
|
|
|||
|
По умолчанию значение установлено на `'root'` . Это переводится в корневой инжектор приложения. В принципе, установка поля в `'root'` делает сервис доступным в любом месте.
|
|||
|
|
|||
|
##### Быстрая заметка
|
|||
|
|
|||
|
Как упоминалось ранее, детские инъекторы отступают от своих родителей. Эта резервная стратегия гарантирует, что родители не должны перерегистрироваться для каждого инжектора. Обратитесь к этой статье « [Услуги и инжекторы»](https://guide.freecodecamp.org/angular/services-and-injectors) для иллюстрации этой концепции.
|
|||
|
|
|||
|
Зарегистрированные услуги - это _одноточие_ . Смысл, инструкции для создания экземпляра службы существуют только на одном инжекторе. Это предполагает, что он не был явно зарегистрирован в другом месте.
|
|||
|
|
|||
|
##### Модуль, директива и компонент
|
|||
|
|
|||
|
Модули и компоненты имеют свой собственный экземпляр инжектора. Это очевидно, учитывая `providers: []` поле метаданных. Это поле принимает множество служб и регистрирует их с помощью инжектора модуля или класса компонента. Этот подход происходит в `@NgModule` , `@Directive` или `@Component` .
|
|||
|
|
|||
|
Эта стратегия исключает _дрожание деревьев_ или необязательное удаление неиспользуемых сервисов из инжекторов. Служебные экземпляры живут на своих форсунках в течение всего срока службы модуля или компонента.
|
|||
|
|
|||
|
#### Создание ссылок
|
|||
|
|
|||
|
Ссылки на DOM могут быть экземплярами из любого класса. Имейте в виду, что ссылки по-прежнему являются службами. Они отличаются от традиционных сервисов представлением состояния чего-то другого. Эти сервисы включают функции для взаимодействия со своей ссылкой.
|
|||
|
|
|||
|
Директивы постоянно нуждаются в ссылках DOM. Эти директивы выполняют мутации на своих элементах хоста. См. Следующий пример. Инъектор директивы создает ссылку на главный элемент в конструктор класса.
|
|||
|
|
|||
|
```typescript
|
|||
|
// directives/highlight.directive.ts
|
|||
|
|
|||
|
import { Directive, ElementRef, Renderer2, Input } from '@angular/core';
|
|||
|
|
|||
|
@Directive({
|
|||
|
selector: '[appHighlight]'
|
|||
|
})
|
|||
|
export class HighlightDirective {
|
|||
|
constructor(
|
|||
|
private renderer: Renderer2,
|
|||
|
private host: ElementRef
|
|||
|
) { }
|
|||
|
|
|||
|
@Input() set appHighlight (color: string) {
|
|||
|
this.renderer.setStyle(this.host.nativeElement, 'background-color', color);
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
```html
|
|||
|
|
|||
|
// app.component.html
|
|||
|
|
|||
|
<p [appHighlight]="'yellow'">Highlighted Text!</p>
|
|||
|
```
|
|||
|
|
|||
|
`Renderer2` также получает экземпляр. Какой инжектор выполняет эти услуги? Ну, исходный код каждого сервиса исходит от `@angular/core` . Затем эти службы должны регистрироваться в корневом инжекторе приложения.
|
|||
|
|
|||
|
```typescript
|
|||
|
import { BrowserModule } from '@angular/platform-browser';
|
|||
|
import { NgModule } from '@angular/core';
|
|||
|
import { AppComponent } from './app.component';
|
|||
|
import { HighlightDirective } from './directives/highlight.directive';
|
|||
|
|
|||
|
@NgModule({
|
|||
|
declarations: [
|
|||
|
AppComponent,
|
|||
|
HighlightDirective
|
|||
|
],
|
|||
|
imports: [
|
|||
|
BrowserModule
|
|||
|
],
|
|||
|
providers: [],
|
|||
|
bootstrap: [
|
|||
|
AppComponent
|
|||
|
]
|
|||
|
})
|
|||
|
export class AppModule { }
|
|||
|
```
|
|||
|
|
|||
|
Пустой массив поставщиков !? Не бояться. Угловые регистры автоматически регистрируют многие службы с помощью инжектора корня. Это включает `ElementRef` и `Renderer2` . В этом примере мы управляем элементом хоста через его интерфейс, связанный с созданием `ElementRef` . `Renderer2` позволяет нам обновлять модель DOM через Angular.
|
|||
|
|
|||
|
Вы можете больше узнать о просмотрах из [этой статьи](https://guide.freecodecamp.org/angular/views) . Они являются предпочтительным методом для DOM / просмотра обновлений в Угловых приложениях.
|
|||
|
|
|||
|
Важно признать роль, которую играют инжекторы в приведенном выше примере. Объявляя типы переменных в конструкторе, класс получает ценные сервисы. Тип данных каждого параметра сопоставляется с набором инструкций внутри инжектора. Если инжектор имеет этот тип, он возвращает экземпляр указанного типа.
|
|||
|
|
|||
|
#### Создание служб
|
|||
|
|
|||
|
В статье « [Услуги и инжекторы» подробно](https://guide.freecodecamp.org/angular/services-and-injectors) объясняется этот раздел. Хотя этот раздел повторяет предыдущий раздел или большую часть. Службы часто предоставляют ссылки на что-то еще. Они также могут обеспечить интерфейс, расширяющий возможности класса.
|
|||
|
|
|||
|
Следующий пример будет определять службу ведения журнала, которая будет добавлена в инжектор компонента через своих `providers: []` метаданные.
|
|||
|
|
|||
|
```typescript
|
|||
|
// services/logger.service.ts
|
|||
|
|
|||
|
import { Injectable } from '@angular/core';
|
|||
|
|
|||
|
@Injectable()
|
|||
|
export class LoggerService {
|
|||
|
callStack: string[] = [];
|
|||
|
|
|||
|
addLog(message: string): void {
|
|||
|
this.callStack = [message].concat(this.callStack);
|
|||
|
this.printHead();
|
|||
|
}
|
|||
|
|
|||
|
clear(): void {
|
|||
|
this.printLog();
|
|||
|
this.callStack = [];
|
|||
|
console.log(“DELETED LOG”);
|
|||
|
}
|
|||
|
|
|||
|
private printHead(): void {
|
|||
|
console.log(this.callStack[0] || null);
|
|||
|
}
|
|||
|
|
|||
|
private printLog(): void {
|
|||
|
this.callStack.reverse().forEach((log) => console.log(message));
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
```typescript
|
|||
|
// app.component.ts
|
|||
|
|
|||
|
import { Component } from '@angular/core';
|
|||
|
import { LoggerService } from './services/logger.service';
|
|||
|
|
|||
|
@Component({
|
|||
|
selector: 'app-root',
|
|||
|
templateUrl: './app.component.html',
|
|||
|
providers: [LoggerService]
|
|||
|
})
|
|||
|
export class AppComponent {
|
|||
|
constructor(private logger: LoggerService) { }
|
|||
|
|
|||
|
logMessage(event: any, message: string): void {
|
|||
|
event.preventDefault();
|
|||
|
this.logger.addLog(`Message: ${message}`);
|
|||
|
}
|
|||
|
|
|||
|
clearLog(): void {
|
|||
|
this.logger.clear();
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
```html
|
|||
|
|
|||
|
// app.component.html
|
|||
|
|
|||
|
<h1>Log Example</h1>
|
|||
|
<form (submit)="logMessage($event, userInput.value)">
|
|||
|
<input #userInput placeholder="Type a message...">
|
|||
|
<button type="submit">SUBMIT</button>
|
|||
|
</form>
|
|||
|
|
|||
|
<h3>Delete Logged Messages</h3>
|
|||
|
<button type="button" (click)="clearLog()">CLEAR</button>
|
|||
|
```
|
|||
|
|
|||
|
Сосредоточьтесь на конструкторе AppComponent и метаданных. Компонент-инжектор получает инструкции из поля метаданных поставщика, содержащего LoggerService. Затем инжектор знает, что создать экземпляр LoggerService из запрошенного в конструкторе.
|
|||
|
|
|||
|
Параметр конструктора `loggerService` имеет тип `LoggerService` который распознает инжектор. Инжектор следует с помощью экземпляра, как уже упоминалось.
|
|||
|
|
|||
|
#### Вывод
|
|||
|
|
|||
|
Инъекция зависимостей (DI) является парадигмой. Способ, которым он работает в Угловом, - это иерархия инжекторов. Класс получает свои ресурсы без необходимости создавать или знать о них. Инъекторы получают инструкцию и создают экземпляр службы в зависимости от того, какой из них был запрошен.
|
|||
|
|
|||
|
DI показывает много в Угловом. Официальная Угловая документация объясняет, почему парадигма настолько распространена. Они также описывают многочисленные варианты использования для DI по Угловому пути, кроме того, что обсуждалось в этой статье. Проверьте это, нажав ниже!
|
|||
|
|
|||
|
## источники
|
|||
|
|
|||
|
* [Угловая команда. «Зависимость от инъекции». _Google_ . Доступ к 1 июня 2018 года](https://angular.io/guide/dependency-injection-pattern)
|
|||
|
|
|||
|
* [Зуев, Алексей. «Что вы всегда хотели знать о дереве инжекции углового зависимостей». _Угловая глубина_ , 21 марта 2018. Доступ к 1 июня 2018 года](https://blog.angularindepth.com/angular-dependency-injection-and-tree-shakeable-tokens-4588a8f70d5d)
|
|||
|
|
|||
|
|
|||
|
## Ресурсы
|
|||
|
|
|||
|
* [Угловая документация](https://angular.io/guide/pipes)
|
|||
|
|
|||
|
* [Угловой репозиторий GitHub](https://github.com/angular/angular)
|
|||
|
|
|||
|
* [Введение в инъекцию зависимостей](https://angular.io/guide/architecture-services)
|
|||
|
|
|||
|
* [Расширенная инъекция зависимостей](https://angular.io/guide/dependency-injection-pattern)
|