1. 程式人生 > >Angular2 之 依賴注入

Angular2 之 依賴注入

依賴注入這部分分為兩部分來學習。第一部分自然是官網上的文件,另外一部分,是自己的“血淚時間史”。

之所以稱之為“血淚時間史”,是因為在這部分上花費的時間實在是有點多,也就是前面提到過的“時間的教訓”,所以在這裡要記錄下來,避免下次再犯這樣的錯誤。

依賴注入

依賴注入是一個用來管理程式碼依賴的強大模式。

應用程式全域性依賴

在這裡主要說的是,在應用程式根元件AppComponent中註冊那些被應用程式全域性使用的依賴提供商。

import { LoggerService }      from './logger.service';
import { UserContextService } from './user-context.service'
; import { UserService } from './user.service'; @Component({ moduleId: module.id, selector: 'my-app', templateUrl: 'app.component.html', providers: [ LoggerService, UserContextService, UserService ] }) export class AppComponent { /* . . . */ }

在@Component元資料的providers陣列匯入和註冊了幾個服務,這些服務都是使用類來實現的,所以服務類能充當自己的提供商。==> 淚在providers

陣列中就算是註冊成功了。

我們在根元件中註冊了這些服務,所以我們可以在應用程式的任何地方,把它們注入到任何元件和服務的建構函式中去!

@Injectable()

  • @Injectable裝飾器只在一個服務類有自己的依賴的時候,才是必不可少的。
@Injectable()
export class UserContextService {
  constructor(private userService: UserService, private loggerService: LoggerService) {
  }
}

這個@Injectable()就是必不可少的。

  • AppComponent類有兩個依賴,但它沒有@Injectable()。 它不需要@Injectable(),這是因為元件類有@Component裝飾器。 在用TypeScript的Angular應用裡,有一個單獨的裝飾器 — 任何裝飾器 — 來標識依賴的型別就夠了。

把服務作用域限制到一個元件支樹

一個元件中注入的服務依賴,會在該元件的所有子元件中可見,而且Angular會把同樣的服務例項注入到需要該服務的子元件中。

這剛好在實際工作有遇到這樣的例子。

我在sino-base-data-service.component元件中注入了BaseDataService,那麼它的所有子元件sino-list.component就都能訪問到這個service。

多個服務例項

主要講的是,在元件級別注入服務,每個元件就能擁有自己獨立的service例項。這樣就能保證每個元件都有自己的服務。每個元件都有自己的工作狀態,與其他元件的服務與狀態相隔離,這種我們成為沙盒化。每個服務和元件都在自己的沙盒裡執行。

@Optional 和 @Host

當元件申請一個依賴時,Angular從該元件本身的注入器開始,沿著依賴注入器的樹往上找,直到找到第一個符合要求的提供商。如果罩杯多就會丟擲一個錯誤。

@Optional

當Angular找不到依賴時,@Optional裝飾器會告訴Angular繼續執行,Angualr會把此注入引數設定為null(而不是預設的丟擲錯誤的行為)。

@Host

該裝飾器將把往上搜索的行為截止在宿主元件

使用提供商來定義依賴

  • useValue - 值提供商
  • useClass
  • useExisting
  • useFactory

useValue

通常在單元測試中使用。useValue的值必須是立即定義的。

e.g.

 { provide: Hero,          useValue:    someHero },
 { provide: TITLE,         useValue:   'Hero of the Month' },

useClass

useClasst提供商建立並返回一個指令類的新例項。
使用該技術來為公共或預設類提供備選實現。該替代品能實現一個不同的策略,比如拓展預設類或者在測試的時候假冒真實類。

{ provide: HeroService,   useClass:    HeroService },
{ provide: LoggerService, useClass:    DateLoggerService },

useExisting - 別名-提供商

使用useExisting,提供商可以把一個令牌對映到另一個令牌上。實際上,第一個令牌是第二個令牌所對應的服務的一個別名,創造了訪問同一個服務物件的兩種方法

{ provide: MinimalLogger, useExisting: LoggerService },

通過使用別名介面來把一個API變窄,是一個很重要的該技巧的使用例子.

useFactory - 工廠-提供商

useFactory提供商通過呼叫工廠函式來新建一個依賴物件。

 { provide: RUNNERS_UP,    useFactory:  runnersUpFactory(2), deps: [Hero, HeroService] }

知識點

  • 令牌 - 使用提供商來定義依賴

我們通常在建構函式裡面,為引數指定型別,讓Angular來處理依賴注入。該引數型別就是依賴注入器所需的令牌。 Angular把該令牌傳給注入器,然後把得到的結果賦給引數。

使用angular的DI系統來獲取引數時,必須將該令牌需要依賴的其他服務一併注入。
e.g.
要模擬BaseDataService必須有ModuleConfig

 export class CrudModule {
  static forRoot(config: any, routeConfig?: any): ModuleWithProviders {
    return {
      ngModule: CrudModule,
      providers: [
        BaseDataService,
        {
          provide: ModuleConfig,
          useValue: config,
        }, {
          provide: RouteConfig,
          useValue: routeConfig ? routeConfig : DEFAULT_ROUTE_CONFIG,
        },
      ],
    };
  }
}
@Injectable()
export class SinoListComponent implements OnInit {
  constructor(
    private config: ModuleConfig,
    private baseDataService: BaseDataService,) {}
}
  • ng-content

    把對應元件中的內容投影進元件的檢視中。

<ion-content>
  <sino-loading-hint [state]="state" loadingTitle="loading..." needRefresh=true (onVoted)="onVoted($event)">
    <h2 class="sino-list-title">{{title}}</h2>
    <div class="sino-list" *ngFor="let item of datas; let i = index" (click)="toDetail(item, i)">
      <h3>{{item.title}}</h3>
      <p>
        來自:{{item.ngDepartmentName}}的發文申請<br/>
        <span>傳送時間:{{item.signDate}}</span>
      </p>
    </div>
  </sino-loading-hint>
</ion-content>

“`

寫在後面

GitHub上集大家之力搞了一個前端面試題的專案,裡面都是大家面試時所遇到的題以及一些學習資料,有興趣的話可以關注一下。如果你也有興趣加入我們的話,請在專案中留言。專案同時也可以在gitbook上檢視。