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) |