天天看點

Angular 依賴注入的學習筆記

Angular官方文檔

Specifying a provider token

If you specify the service class as the provider token, the default behavior is for the injector to instantiate that class with new.

In the following example, the Logger class provides a Logger instance.

providers: [Logger]

NgModule的providers區域裡,指定provider token. 如果将一個服務類直接指定成provider token,預設的行為就是,injector直接用new來初始化那個類。

上面的例子,會觸發Logger的構造函數。等價于:

[{ provide: Logger, useClass: Logger }]

這個完整的聲明在Angular官方文檔裡稱為expanded provider configuration, 是一個包含兩個屬性的對象。

The provide property holds the token that serves as the key for both locating a dependency value and configuring the injector.

provide屬性包含一個token,用于定位一個dependency value和配置injector.

The second property is a provider definition object, which tells the injector how to create the dependency value. The provider-definition key can be useClass, as in the example. It can also be useExisting, useValue, or useFactory. Each of these keys provides a different type of dependency, as discussed below.

第二個屬性是一個provider定義對象,告訴injector如何建立dependency value. 這個定義可以是useClass,useExisting,useValue,或者useFactory.

一些例子:

[{ provide: Logger, useClass: BetterLogger }]

當使用Logger token時,injector傳回BetterLogger的執行個體。

Aliasing class providers

To alias a class provider, specify the alias and the class provider in the providers array with the useExisting property.

In the following example, the injector injects the singleton instance of NewLogger when the component asks for either the new or the old logger. In this way, OldLogger is an alias for NewLogger.

下列的配置,會導緻Component需要使用OldLogger時,會傳回NewLogger的singleton執行個體。當然,如果請求NewLogger,亦會傳回NewLogger執行個體。是以這種情形下,OldLogger是NewLogger語義層面的alias.

[ NewLogger,

 // Alias OldLogger w/ reference to NewLogger

 { provide: OldLogger, useExisting: NewLogger}]

1

2

3

如果使用如下指令行建立 service:

ng generate service User

自動生成的代碼:

import { Injectable } from '@angular/core';

@Injectable({

 providedIn: 'root',

})

export class UserService {

}

4

5

6

7

現在,你就可以在應用中到處注入 UserService 了。

該服務本身是 CLI 建立的一個類,并且加上了 @Injectable() 裝飾器。預設情況下,該裝飾器是用 providedIn 屬性進行配置的,它會為該服務建立一個提供者。在這個例子中,providedIn: ‘root’ 指定 Angular 應該在根注入器中提供該服務。

當你把服務提供者添加到應用的根注入器中時,它就在整個應用程式中可用了。 另外,這些服務提供者也同樣對整個應用中的類是可用的 —— 隻要它們有供查找用的服務令牌。

你應該始終在根注入器中提供這些服務 —— 除非你希望該服務隻有在消費方要導入特定的 @NgModule 時才生效。

也可以規定某個服務隻有在特定的 @NgModule 中提供。比如,如果你希望隻有當消費方導入了你建立的 UserModule 時才讓 UserService 在應用中生效,那就可以指定該服務要在該子產品中提供:

import { UserModule } from './user.module';

 providedIn: UserModule,

8

上面的例子展示的就是在子產品中提供服務的首選方式。之是以推薦該方式,是因為當沒有人注入它時,該服務就可以被搖樹優化掉。如果沒辦法指定哪個子產品該提供這個服務,你也可以在那個子產品中為該服務聲明一個提供者。

服務是一個廣義的概念,它包括應用所需的任何值、函數或特性。狹義的服務是一個明确定義了用途的類。它應該做一些具體的事,并做好。

Angular 把元件和服務區分開,以提高子產品性和複用性。 通過把元件中和視圖有關的功能與其它類型的處理分離開,你可以讓元件類更加精簡、高效。

理想情況下,元件的工作隻管使用者體驗,而不用顧及其它。 它應該提供用于資料綁定的屬性和方法,以便作為視圖(由模闆渲染)和應用邏輯(通常包含一些模型的概念)的中介者。

元件應該把諸如從伺服器擷取資料、驗證使用者輸入或直接往控制台中寫日志等工作委托給各種服務。通過把各種處理任務定義到可注入的服務類中,你可以讓它被任何元件使用。 通過在不同的環境中注入同一種服務的不同提供者,你還可以讓你的應用更具适應性。

當 Angular 建立元件類的新執行個體時,它會通過檢視該元件類的構造函數,來決定該元件依賴哪些服務或其它依賴項。 比如 HeroListComponent 的構造函數中需要 HeroService:

constructor(private service: HeroService) { }

當 Angular 發現某個元件依賴某個服務時,它會首先檢查是否該注入器中已經有了那個服務的任何現有執行個體。如果所請求的服務尚不存在,注入器就會使用以前注冊的服務提供者來制作一個,并把它加入注入器中,然後把該服務傳回給 Angular。

是否建立新的依賴服務執行個體,取決于 provider 的具體類型。

當所有請求的服務已解析并傳回時,Angular 可以用這些服務執行個體為參數,調用該元件的構造函數。

HeroService 的注入過程如下所示:從應用的 Injector 裡擷取 HeroService 執行個體。

Angular 依賴注入的學習筆記

提供服務

對于要用到的任何服務,你必須至少注冊一個提供者。服務可以在自己的中繼資料中把自己注冊為提供者,這樣可以讓自己随處可用。或者,你也可以為特定的子產品或元件注冊提供者。要注冊提供者,就要在服務的 @Injectable() 裝飾器中提供它的中繼資料,或者在 @NgModule() 或 @Component() 的中繼資料中。

預設情況下,Angular CLI 的 ng generate service 指令會在 @Injectable() 裝飾器中提供中繼資料來把它注冊到根注入器中。本教程就用這種方法注冊了 HeroService 的提供者:

什麼是 Angular 依賴注入的 tree-shaking

When you provide the service at the root level, Angular creates a single, shared instance of HeroService and injects it into any class that asks for it. Registering the provider in the @Injectable() metadata also allows Angular to optimize an app by removing the service from the compiled app if it isn’t used, a process known as tree-shaking.

當你使用特定的 NgModule 注冊提供者時,該服務的同一個執行個體将會對該 NgModule 中的所有元件可用。要想在這一層注冊,請用 @NgModule() 裝飾器中的 providers 屬性:

@NgModule({

 providers: [

  BackendService,

  Logger

],

...

當你在元件級注冊提供者時,你會為該元件的每一個新執行個體提供該服務的一個新執行個體。 要在元件級注冊,就要在 @Component() 中繼資料的 providers 屬性中注冊服務提供者。

@Component({

 selector:    'app-hero-list',

 templateUrl: './hero-list.component.html',

 providers:  [ HeroService ]

繼續閱讀