angular11源碼探索[服務基礎一]

服務

Angular依賴項注入如今是Angular的核心部分,並容許將依賴項注入到組件或類中編程

依賴注入(DI)是一種技術,在這種技術中,咱們將一個對象的實例提供給另外一個依賴於它的對象。這種技術也稱爲「控制反轉」(IoC)api

IoC — 控制反轉數組

DI — 依賴注入微信

IOC

三個原則ide

  • 高層次的模塊不該該依賴於低層次的模塊,它們都應該依賴於抽象
  • 抽象不該該依賴於具體實現,具體實現應該依賴於抽象
  • 面向接口編程 而不要面向實現編程

IOC,是一種設計原則:this

經過將組件(Components)的設置和使用分開,來下降類別或模組之間的耦合度(即,解耦)設計

控制反轉,指實例依賴成員的[控制流程],由主動控制變成被動控制,所以被稱爲控制反轉3d

案例:code

  • 打開iPhone,打開微信,進入羣聊,搶紅包
  • 打開iPhone,打開短信,編輯短信內容,選擇收件人,發送短信
  • 打開iPhone,打開小米商城,選擇須要搶購的商品,等待到達搶購時間,開始搶購

這些過程和iPhone耦合的很緊密,若是小明須要換手機了,那麼那須要在每一個過程當中都將iPhone換爲新手機。這時,就須要控制反轉,咱們再也不主動去得到手機,而是被動的接收一個手機。component

接收手機

  • 打開手機,打開微信,進入羣聊,搶紅包
  • 打開手機,打開短信,編輯短信內容,選擇收件人,發送短信
  • 打開手機,打開小米商城,選擇須要搶購的商品,等待到達搶購時間,開始搶購

咱們發現,過程和具體的一個手機(例如,iPhone)耦合度下降了,能夠很簡單的更改手機的實例。

另外一個例子

咱們去網咖打遊戲,是不可能本身帶遊戲去網咖的,而是網咖都把這些遊戲提供好了

須要的遊戲,不用本身下載,而是網咖提供給你。

==>

須要的依賴實例,不用主動(Active)創建,而是被動(Passive)接收。

簡單來講,A依賴B,但A沒法控制B的建立和銷燬,僅使用B,那麼B的控制權交給C(A之外)處理

  • 第一個例子,小明是A,依賴手機B,C能夠是其餘任何給小明手機的人(可能有點牽強)
  • 第二個例子,咱們是A,依賴遊戲B,C則是網吧

依賴注入DI

剛纔那兩個例子中,小明須要手機,咱們須要玩遊戲,那麼有哪些方法可讓咱們被動接收這些東西呢?假設小明是A,手機是B

  • 經過A的接口,把B傳入
  • 經過A的構造,把B傳入
  • 經過設置A的屬性,把B傳入

這個過程就是依賴注入,即A啥都沒幹,就能夠直接使用B,好比咱們啥也不用幹,就能夠直接玩遊戲,真香。

也能夠說,依賴注入是實現控制反轉的一種方式,它們之間有着密切的聯繫

案例一

class Id {
  static getId(type: string): Id {
    return new Id()
  }
}

class Address {
  constructor(city, street) { }
}

class Person {
  id: Id
  address: Address
  constructor(id: Id, address: Address) {
    this.id = id
    this.address = address
  }
}

// 在某個類當中調用的時候
main() {
  const id = Id.getId('123')
  const address = new Address('北京', '北京')
  const person = new Person(id, address)
}

咱們也將依賴提高到了入口處的 main() 當中,可是在當下這種形式中,咱們已經知道若是有新的需求變更,咱們仍是須要去模塊的內部來進行修改,下面咱們就來看看如何在 Angular 當中來解決這個問題的

Angular 的依賴注入中主要有三個概念

  • Injector,注入者,利用其提供的 API 去建立依賴的實例
  • Provider,告訴 Injector 如何去建立一個實例(構造這個對象)
  • Object,建立好的對象,也就是當前所處的模塊或者組件須要的依賴(某種類型的對象,依賴自己也是一種類型)

小案例demo

服務
@Injectable()
export class StateNewService {
  constructor() {}
  getLog() {
    return '1111'
  }
}

模塊 注入
@NgModule({
 
  providers:[StateNewService]
})
頁面使用
export class TwoComponent implements OnInit {
  constructor(private states:StateNewService) {
    console.log(states.getLog());
  }
}

類型令牌

StateNewService 用做搜索查找的令牌,而後咱們新類StateNewService ,組件可使用它

providers:[{ provide: StateNewService, useClass: StateNewService }]
其實也等價於下面這種寫法
   [{
      provide:StateNewService,
      useFactory:()=>new StateNewService()
    }]

替換當前class

@Injectable()
export class CowService  {

  makeNoise() {
    return 'mooo!';
  }

}
@Injectable()
export class TestService {

  constructor() { }
  makeNoise() {
    return '333';
  }
}

 providers: [
    {provide:TestService,useClass:CowService}
     ]

使用的時候
constructor(
    private other: TestService
  ) {
    console.log(other.makeNoise()); // mooo!

字符串令牌

依賴項是值或者對象或者類表示

服務
@Injectable()
export class StateNewService {
  constructor() {}
  getLog() {
    return '1111'
  }
}

模塊
@NgModule({
  providers:[
    { provide: 'STATE_NEW_SERVICE', useClass: StateNewService },
    {provide:'APIURL', useValue: 'http://SomeEndPoint.com/api' },
    {provide:'USE_FAKE', useValue: true },
  ]
})
使用
export class TwoComponent implements OnInit {
  constructor(
    @Inject('STATE_NEW_SERVICE') private states: StateNewService,
    @Inject('APIURL') private apiURL:string,
    @Inject('USE_FAKE') private use:boolean ) {
    console.log(states.getLog());
    // two.component.ts:26 1111
    console.log(apiURL);
    // two.component.ts:27 http://SomeEndPoint.com/api
    console.log(use);
    // true
  }
}

字符串令牌更易於錯誤鍵入,這使得在大型應用程序中難以跟蹤和維護。

Angular提供了InjectionToken類,以確保建立了惟一令牌。經過建立類的新實例來建立注入令牌InjectionToken

InjectionToken

  • 建立 Token 的時候爲了不命名衝突,儘可能避免使用字符串做爲 Token
  • 若要建立模塊內通用的依賴對象,須要在 NgModule 中註冊相關的 provider
  • 若在每一個組件中都有惟一的依賴對象,就須要在 Component 中註冊相關的 provider

首先,建立一個單獨的文件,並將其命名爲tokens.ts

  • InjectionToken@angular/core庫導入。建立的實例InjectionToken並將其分配給constAPI_URL
import { InjectionToken } from '@angular/core';
export const API_URL= new InjectionToken<string>('');
  • 在module,令牌用於在providers元數據中註冊值
import { API_URL } from './tokens';

providers: [ 
   { provide: API_URL, useValue: 'http://SomeEndPoint.com/api' }
]
  • 直接使用

    import { API_URL } from './tokens';
     
    constructor(@Inject(API_URL) private apiURL: string) { 
    }

你能夠把multi:true 打印的時候以前是字符串如今你發現返回的是數組,原理就是相同的token 註冊多個值

providers:[
    {provide:API_URL, useValue: 'http://SomeEndPoint.com/api',multi:true },
  ]

當咱們給令牌添加第二個token

providers:[
    {provide:API_URL, useValue: 'http://SomeEndPoint.com/api1',multi:true },
    {provide:API_URL, useValue: 'http://SomeEndPoint.com/api2',multi:true },
  ]
 constructor(
     @Inject(API_URL) private apiURL:string
  ) {
    console.log(apiURL);
     // ["http://SomeEndPoint.com/api1", "http://SomeEndPoint.com/api2"]

咱們發現若是移除其中一個multi:true ,咱們會發現打印的時候報錯

若是發現若是兩個都移除multi:true ,咱們會發現打印的是api2,下面的會把上面的替換掉

相關文章
相關標籤/搜索