前端之Angular2實戰:依賴注入詳解與應用

Dependence Injection(依賴注入)

dependency-injection-in-angular-2javascript

依賴注入是Angular中的最大的一個特性與賣點。它容許應用中不一樣的組件不須要顯性地創建關聯便可以相互調用。不一樣,Angular 1中的依賴注入仍然是存在着一些問題,這也是Angular 2徹底重構了一套依賴注入系統的緣由。Angular 1中的依賴注入系統主要存在的問題以下:html

  • Internal Cache(內建緩存):依賴通常是被當作單例對待,任何一個服務在整個應用的生命週期中都應該只被建立一次。java

  • Synchronous by default(默認異步):Angular 1中的服務建立不是異步建立的。typescript

  • Namespace collision(命名空間衝突):在應用的生命週期中某個」type」的token是惟一的,若是咱們自定義了一個名爲Car的服務,而引入的第三方框架中也存在着同名的Car的服務,那麼整個系統就會存在問題。bootstrap

  • Built into the framework(框架內建):Angular 1的依賴注入是內建的,咱們沒法單獨的進行使用。緩存

Angular 2的依賴注入體系大概以下所示:angular2

DI in Angular 2

其中有幾個關鍵的概念解釋以下:app

  • Injector(注入器):Injector就相似於Spring裏面的ApplicationContext,提供了一系列的接口以供依賴實例的建立。框架

  • Binding(綁定):Binding的做用在於告訴Injector如何去爲某個依賴建立實例。一個Binding須要映射到一個工廠類型的方法。異步

  • Dependence(依賴):一個Dependence是某個對象被建立的類型。

最簡單的Angular 2中的依賴注入的方式以下:

import { Injector } from 'angular2/di';

var injector = Injector.resolveAndCreate([
  Car,
  Engine,
  Tires,
  Doors
]);
          
var car = injector.get(Car);

resolveAndCreate是一個靜態的接口方法,根據輸入的一系列的Binding來建立依賴的實例。然後,可使用injector.get()方法來獲取某個Type/Token對應的對象實例。而在使用這個依賴時,可使用Angular 2內置的Inject:

import { Inject } from 'angular2/di';

class Car {
  constructor(
    @Inject(Engine) engine,
    @Inject(Tires) tires,
    @Inject(Doors) doors
  ) {
    ...
  }
}

Inject裝飾器會自動將元數據綁定到Car類的屬性中,也能夠改寫爲TypeScript的方式:

class Car {
  constructor(engine: Engine, tires: Tires, doors: Doors) {
    ...
  }
}

到這一步,某個類能夠聲明它本身的依賴,而且被DI解析全部該類的依賴項。可是Injector還須要從Binding中獲取如何去建立這些對象實例的信息。上文中是直接在resolveAndCreate方法中傳入了一系列的Type/Token,而若是使用完整的寫法,應該使用toClass方法顯性的將某個Type/Token映射到某個實例。

import { bind } from 'angular2/di';

var injector = Injector.resolveAndCreate([
  bind(Car).toClass(Car),
  bind(Engine).toClass(Engine),
  bind(Tires).toClass(Tires),
  bind(Doors).toClass(Doors)
]);

上述方法中的token能夠是任意的類型或者一個字符串,這也就是所謂的Recipe機制的具體實現。在這樣的一種Binding的幫助下,不只僅Injector知道如何在應用過程當中使用這些依賴,而且配置瞭如何建立這些依賴。

進一步考慮,若是在應用中已經肯定了Foo類型,那又何須要寫bind(Foo).toClass(Foo)這樣的表達式,直接在程序中引入寫死便可,而依賴注入的真正魅力在於:

bind(Engine).toClass(OtherEngine)

這樣能夠動態的爲某個token綁定到依賴中,而且有效解決了命名空間衝突的問題。咱們能夠建立一個相似與接口的類型,而後將它指向到具體的類型中。就像Java中的Interface與Implementation。

Other binding instructions

有時候,咱們並不必定須要將某個token綁定到某個類中,而是綁定到某個字符串值或者工廠方法中。

  • 綁定到值

bind(String).toValue('Hello World')
  • 綁定到別名

bind(Engine).toClass(Engine)
bind(V8).toAlias(Engine)

toAlias方法將某個token綁定到另外一個token中。

  • 綁定到某個工廠方法

bind(Engine).toFactory(() => {
  if (IS_V8) {
    return new V8Engine();
  } else {
    return new V6Engine();
  }
})

固然,某個工廠方法可能也有其依賴項,只要簡單地將依賴項指向到參數中而且添加到token列表中便可。

bind(Engine).toFactory((dep1, dep2) => {
  if (IS_V8) {
    return new V8Engine();
  } else {
    return new V6Engine();
  }
}, [Token1, Token2])

Transient Dependencies and Child Injectors(短暫性傳遞與子注入器)

在上文中說起的依賴項每每都是單例化的,可是有時候咱們須要的是一個短暫的,即非單例模式的依賴,整體來講有兩種方式:

  • 使用工廠模式

bind(Engine).toFactory(() => {
  return new Engine();
})
  • 子注入器

可使用Injector.resolveAndCreateChild()這個方法迭代地建立子注入器,一個子注入器會繼承父類注入器中聲明的依賴項,可是若是子注入器中也是聲明瞭某個依賴,那麼它建立的實例與父注入器建立的一樣的token的實例是不一致的:

var injector = Injector.resolveAndCreate([Engine]);
var childInjector = injector.resolveAndCreateChild([Engine]);

injector.get(Engine) !== childInjector.get(Engine);

Child injectors

Component Dependence

@Component({
  selector: 'app'
})
@View({
  template: '<h1>Hello !</h1>'
})
class App {
  constructor() {
    this.name = 'World';
  }
}
          
bootstrap(App);

上述聲明的Component是直接將name寫死在了代碼裏,若是將獲取名字的這部分提取出來做爲一個單獨的服務:

class NameService {
  constructor() {
    this.name = 'Pascal';
  }

  getName() {
    return this.name;
  }
}

若是須要使用NameService,那麼在聲明某個Component時候,就須要使用@Inject裝飾器:

class App {
  constructor(@Inject(NameService) NameService) {
    this.name = NameService.getName();
  }
}

若是是TypeScript,須要這麼寫:

class App {
  constructor(NameService: NameService) {
    this.name = NameService.getName();
  }
}

而NameService的Injector以及Binding這一步,其實就是由bootstrap方法實現的:

bootstrap(App, [NameService]);

固然,也能夠寫的更加優雅:

@Component({
  selector: 'app',
  bindings: [NameService]
})
@View({
  template: '<h1>Hello !</h1>'
})
class App {
  ...
}
相關文章
相關標籤/搜索