国产gaysexchina男同gay,japanrcep老熟妇乱子伦视频,吃奶呻吟打开双腿做受动态图,成人色网站,国产av一区二区三区最新精品

Angular9 依賴注入

2020-07-03 15:01 更新

依賴注入(DI)是一種重要的應(yīng)用設(shè)計(jì)模式。 Angular 有自己的 DI 框架,在設(shè)計(jì)應(yīng)用時(shí)常會(huì)用到它,以提升它們的開發(fā)效率和模塊化程度。

依賴,是當(dāng)類需要執(zhí)行其功能時(shí),所需要的服務(wù)或?qū)ο蟆?DI 是一種編碼模式,其中的類會(huì)從外部源中請(qǐng)求獲取依賴,而不是自己創(chuàng)建它們。

在 Angular 中,DI 框架會(huì)在實(shí)例化該類時(shí)向其提供這個(gè)類所聲明的依賴項(xiàng)。本指南介紹了 DI 在 Angular 中的工作原理,以及如何借助它來(lái)讓你的應(yīng)用更靈活、高效、健壯,以及可測(cè)試、可維護(hù)。

我們先看一下英雄指南中英雄管理特性的簡(jiǎn)化版。這個(gè)簡(jiǎn)化版不使用 DI,我們將逐步把它轉(zhuǎn)換成使用 DI 的。

  1. Path:"src/app/heroes/heroes.component.ts" 。

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


    @Component({
      selector: 'app-heroes',
      template: `
        <h2>Heroes</h2>
        <app-hero-list></app-hero-list>
      `
    })
    export class HeroesComponent { }

  1. Path:"src/app/heroes/heroes.component.ts" 。

    import { Component }   from '@angular/core';
    import { HEROES }      from './mock-heroes';


    @Component({
      selector: 'app-hero-list',
      template: `
        <div *ngFor="let hero of heroes">
          {{hero.id}} - {{hero.name}}
        </div>
      `
    })
    export class HeroListComponent {
      heroes = HEROES;
    }

  1. Path:"src/app/heroes/hero.ts" 。

    export interface Hero {
      id: number;
      name: string;
      isSecret: boolean;
    }

  1. Path:"src/app/heroes/mock-heroes.ts" 。

    import { Hero } from './hero';


    export const HEROES: Hero[] = [
      { id: 11, isSecret: false, name: 'Dr Nice' },
      { id: 12, isSecret: false, name: 'Narco' },
      { id: 13, isSecret: false, name: 'Bombasto' },
      { id: 14, isSecret: false, name: 'Celeritas' },
      { id: 15, isSecret: false, name: 'Magneta' },
      { id: 16, isSecret: false, name: 'RubberMan' },
      { id: 17, isSecret: false, name: 'Dynama' },
      { id: 18, isSecret: true,  name: 'Dr IQ' },
      { id: 19, isSecret: true,  name: 'Magma' },
      { id: 20, isSecret: true,  name: 'Tornado' }
    ];

HeroesComponent 是頂層英雄管理組件。 它唯一的目的是顯示 HeroListComponent,該組件會(huì)顯示一個(gè)英雄名字的列表。

HeroListComponent 的這個(gè)版本從 HEROES 數(shù)組(它在一個(gè)獨(dú)立的 "mock-heroes" 文件中定義了一個(gè)內(nèi)存集合)中獲取英雄。

Path:"src/app/heroes/hero-list.component.ts (class)" 。

export class HeroListComponent {
  heroes = HEROES;
}

這種方法在原型階段有用,但是不夠健壯、不利于維護(hù)。 一旦你想要測(cè)試該組件或想從遠(yuǎn)程服務(wù)器獲得英雄列表,就不得不修改 HeroesListComponent 的實(shí)現(xiàn),并且替換每一處使用了 HEROES 模擬數(shù)據(jù)的地方。

創(chuàng)建和注冊(cè)可注入的服務(wù)

DI 框架讓你能從一個(gè)可注入的服務(wù)類(獨(dú)立文件)中為組件提供數(shù)據(jù)。為了演示,我們還會(huì)創(chuàng)建一個(gè)用來(lái)提供英雄列表的、可注入的服務(wù)類,并把它注冊(cè)為該服務(wù)的提供者。

同一個(gè)文件中放多個(gè)類容易讓人困惑。我們通常建議你在單獨(dú)的文件中定義組件和服務(wù)。

如果你把組件和服務(wù)都放在同一個(gè)文件中,請(qǐng)務(wù)必先定義服務(wù),然后再定義組件。如果在服務(wù)之前定義組件,則會(huì)在運(yùn)行時(shí)收到一個(gè)空引用錯(cuò)誤。

也可以借助 forwardRef() 方法來(lái)先定義組件,就像這個(gè)博客中解釋的那樣。

創(chuàng)建可注入的服務(wù)類

Angular CLI 可以用下列命令在 "src/app/heroes" 目錄下生成一個(gè)新的 HeroService 類。

ng generate service heroes/hero

下列命令會(huì)創(chuàng)建 HeroService 的骨架。

Path:"src/app/heroes/hero.service.ts (CLI-generated)" 。

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


@Injectable({
  providedIn: 'root',
})
export class HeroService {
  constructor() { }
}

@Injectable() 是每個(gè) Angular 服務(wù)定義中的基本要素。該類的其余部分導(dǎo)出了一個(gè) getHeroes 方法,它會(huì)返回像以前一樣的模擬數(shù)據(jù)。(真實(shí)的應(yīng)用可能會(huì)從遠(yuǎn)程服務(wù)器中異步獲取這些數(shù)據(jù),不過(guò)這里我們先忽略它,專心實(shí)現(xiàn)服務(wù)的注入機(jī)制。)

Path:"src/app/heroes/hero.service.ts" 。

import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';


@Injectable({
  // we declare that this service should be created
  // by the root application injector.
  providedIn: 'root',
})
export class HeroService {
  getHeroes() { return HEROES; }
}

用服務(wù)提供者配置注入器

我們創(chuàng)建的類提供了一個(gè)服務(wù)。@Injectable() 裝飾器把它標(biāo)記為可供注入的服務(wù),不過(guò)在你使用該服務(wù)的 provider 提供者配置好 Angular 的依賴注入器之前,Angular 實(shí)際上無(wú)法將其注入到任何位置。

該注入器負(fù)責(zé)創(chuàng)建服務(wù)實(shí)例,并把它們注入到像 HeroListComponent 這樣的類中。 你很少需要自己創(chuàng)建 Angular 的注入器。Angular 會(huì)在執(zhí)行應(yīng)用時(shí)為你創(chuàng)建注入器,第一個(gè)注入器是根注入器,創(chuàng)建于啟動(dòng)過(guò)程中。

提供者會(huì)告訴注入器如何創(chuàng)建該服務(wù)。 要想讓注入器能夠創(chuàng)建服務(wù)(或提供其它類型的依賴),你必須使用某個(gè)提供者配置好注入器。

提供者可以是服務(wù)類本身,因此注入器可以使用 new 來(lái)創(chuàng)建實(shí)例。 你還可以定義多個(gè)類,以不同的方式提供同一個(gè)服務(wù),并使用不同的提供者來(lái)配置不同的注入器。

注入器是可繼承的,這意味著如果指定的注入器無(wú)法解析某個(gè)依賴,它就會(huì)請(qǐng)求父注入器來(lái)解析它。 組件可以從它自己的注入器來(lái)獲取服務(wù)、從其祖先組件的注入器中獲取、從其父 NgModule 的注入器中獲取,或從 root 注入器中獲取。

你可以在三種位置之一設(shè)置元數(shù)據(jù),以便在應(yīng)用的不同層級(jí)使用提供者來(lái)配置注入器:

  • 在服務(wù)本身的 @Injectable() 裝飾器中。

  • NgModule@NgModule() 裝飾器中。

  • 在組件的 @Component() 裝飾器中。

@Injectable() 裝飾器具有一個(gè)名叫 providedIn 的元數(shù)據(jù)選項(xiàng),在那里你可以指定把被裝飾類的提供者放到 root 注入器中,或某個(gè)特定 NgModule 的注入器中。

@NgModule()@Component() 裝飾器都有用一個(gè) providers 元數(shù)據(jù)選項(xiàng),在那里你可以配置 NgModule 級(jí)或組件級(jí)的注入器。

所有組件都是指令,而 providers 選項(xiàng)是從 @Directive() 中繼承來(lái)的。 你也可以與組件一樣的級(jí)別為指令、管道配置提供者。

注入服務(wù)

HeroListComponent 要想從 HeroService 中獲取英雄列表,就得要求注入 HeroService,而不是自己使用 new 來(lái)創(chuàng)建自己的 HeroService 實(shí)例。

你可以通過(guò)制定帶有依賴類型的構(gòu)造函數(shù)參數(shù)來(lái)要求 Angular 在組件的構(gòu)造函數(shù)中注入依賴項(xiàng)。下面的代碼是 HeroListComponent 的構(gòu)造函數(shù),它要求注入 HeroService。

Path:"src/app/heroes/hero-list.component (constructor signature)" 。

constructor(heroService: HeroService)

當(dāng)然,HeroListComponent 還應(yīng)該使用注入的這個(gè) HeroService 做一些事情。 這里是修改過(guò)的組件,它轉(zhuǎn)而使用注入的服務(wù)。與前一版本并列顯示,以便比較。

//hero-list.component (with DI)


import { Component }   from '@angular/core';
import { Hero }        from './hero';
import { HeroService } from './hero.service';


@Component({
  selector: 'app-hero-list',
  template: `
    <div *ngFor="let hero of heroes">
      {{hero.id}} - {{hero.name}}
    </div>
  `
})
export class HeroListComponent {
  heroes: Hero[];


  constructor(heroService: HeroService) {
    this.heroes = heroService.getHeroes();
  }
}

//hero-list.component (without DI)


import { Component }   from '@angular/core';
import { HEROES }      from './mock-heroes';


@Component({
  selector: 'app-hero-list',
  template: `
    <div *ngFor="let hero of heroes">
      {{hero.id}} - {{hero.name}}
    </div>
  `
})
export class HeroListComponent {
  heroes = HEROES;
}

必須在某些父注入器中提供 HeroService。HeroListComponent 并不關(guān)心 HeroService 來(lái)自哪里。 如果你決定在 AppModule 中提供 HeroService,也不必修改 HeroListComponent。

注入器樹與服務(wù)實(shí)例

在某個(gè)注入器的范圍內(nèi),服務(wù)是單例的。也就是說(shuō),在指定的注入器中最多只有某個(gè)服務(wù)的最多一個(gè)實(shí)例。

應(yīng)用只有一個(gè)根注入器。在 rootAppModule 級(jí)提供 UserService 意味著它注冊(cè)到了根注入器上。 在整個(gè)應(yīng)用中只有一個(gè) UserService 實(shí)例,每個(gè)要求注入 UserService 的類都會(huì)得到這一個(gè)服務(wù)實(shí)例,除非你在子注入器中配置了另一個(gè)提供者。

Angular DI 具有分層注入體系,這意味著下級(jí)注入器也可以創(chuàng)建它們自己的服務(wù)實(shí)例。 Angular 會(huì)有規(guī)律的創(chuàng)建下級(jí)注入器。每當(dāng) Angular 創(chuàng)建一個(gè)在 @Component() 中指定了 providers 的組件實(shí)例時(shí),它也會(huì)為該實(shí)例創(chuàng)建一個(gè)新的子注入器。 類似的,當(dāng)在運(yùn)行期間加載一個(gè)新的 NgModule 時(shí),Angular 也可以為它創(chuàng)建一個(gè)擁有自己的提供者的注入器。

子模塊和組件注入器彼此獨(dú)立,并且會(huì)為所提供的服務(wù)分別創(chuàng)建自己的實(shí)例。當(dāng) Angular 銷毀 NgModule 或組件實(shí)例時(shí),也會(huì)銷毀這些注入器以及注入器中的那些服務(wù)實(shí)例。

借助注入器繼承機(jī)制,你仍然可以把全應(yīng)用級(jí)的服務(wù)注入到這些組件中。 組件的注入器是其父組件注入器的子節(jié)點(diǎn),它會(huì)繼承所有的祖先注入器,其終點(diǎn)則是應(yīng)用的根注入器。 Angular 可以注入該繼承譜系中任何一個(gè)注入器提供的服務(wù)。

比如,Angular 既可以把 HeroComponent 中提供的 HeroService 注入到 HeroListComponent,也可以注入 AppModule 中提供的 UserService。

測(cè)試帶有依賴的組件

基于依賴注入設(shè)計(jì)一個(gè)類,能讓它更易于測(cè)試。 要想高效的測(cè)試應(yīng)用的各個(gè)部分,你所要做的一切就是把這些依賴列到構(gòu)造函數(shù)的參數(shù)表中而已。

比如,你可以使用一個(gè)可在測(cè)試期間操縱的模擬服務(wù)來(lái)創(chuàng)建新的 HeroListComponent。

Path:"src/app/test.component.ts" 。

const expectedHeroes = [{name: 'A'}, {name: 'B'}]
const mockService = <HeroService> {getHeroes: () => expectedHeroes }


it('should have heroes when HeroListComponent created', () => {
  // Pass the mock to the constructor as the Angular injector would
  const component = new HeroListComponent(mockService);
  expect(component.heroes.length).toEqual(expectedHeroes.length);
});

那些需要其它服務(wù)的服務(wù)

服務(wù)還可以具有自己的依賴。HeroService 非常簡(jiǎn)單,沒(méi)有自己的依賴。不過(guò),如果你希望通過(guò)日志服務(wù)來(lái)報(bào)告這些活動(dòng),那么就可以使用同樣的構(gòu)造函數(shù)注入模式,添加一個(gè)構(gòu)造函數(shù)來(lái)接收一個(gè) Logger 參數(shù)。

這是修改后的 HeroService,它注入了 Logger,我們把它和前一個(gè)版本的服務(wù)放在一起進(jìn)行對(duì)比。

  1. Path:"src/app/heroes/hero.service (v2)" 。

    import { Injectable } from '@angular/core';
    import { HEROES }     from './mock-heroes';
    import { Logger }     from '../logger.service';


    @Injectable({
      providedIn: 'root',
    })
    export class HeroService {


      constructor(private logger: Logger) {  }


      getHeroes() {
        this.logger.log('Getting heroes ...');
        return HEROES;
      }
    }

  1. Path:"src/app/heroes/hero.service (v1)" 。

    import { Injectable } from '@angular/core';
    import { HEROES }     from './mock-heroes';


    @Injectable({
      providedIn: 'root',
    })
    export class HeroService {
      getHeroes() { return HEROES; }
    }

  1. Path:"src/app/logger.service" 。

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


    @Injectable({
      providedIn: 'root'
    })
    export class Logger {
      logs: string[] = []; // capture logs for testing


      log(message: string) {
        this.logs.push(message);
        console.log(message);
      }
    }

該構(gòu)造函數(shù)請(qǐng)求注入一個(gè) Logger 的實(shí)例,并把它保存在一個(gè)名叫 logger 的私有字段中。 當(dāng)要求獲取英雄列表時(shí),getHeroes() 方法就會(huì)記錄一條消息。

注意,雖然 Logger 服務(wù)沒(méi)有自己的依賴項(xiàng),但是它同樣帶有 @Injectable() 裝飾器。實(shí)際上,@Injectable() 對(duì)所有服務(wù)都是必須的。

當(dāng) Angular 創(chuàng)建一個(gè)構(gòu)造函數(shù)中有參數(shù)的類時(shí),它會(huì)查找有關(guān)這些參數(shù)的類型,和供注入使用的元數(shù)據(jù),以便找到正確的服務(wù)。 如果 Angular 無(wú)法找到參數(shù)信息,它就會(huì)拋出一個(gè)錯(cuò)誤。 只有當(dāng)類具有某種裝飾器時(shí),Angular 才能找到參數(shù)信息。 @Injectable() 裝飾器是所有服務(wù)類的標(biāo)準(zhǔn)裝飾器。

裝飾器是 TypeScript 強(qiáng)制要求的。當(dāng) TypeScript 把代碼轉(zhuǎn)譯成 JavaScript 時(shí),一般會(huì)丟棄參數(shù)的類型信息。只有當(dāng)類具有裝飾器,并且 "tsconfig.json" 中的編譯器選項(xiàng) emitDecoratorMetadatatrue 時(shí),TypeScript 才會(huì)保留這些信息。CLI 所配置的 "tsconfig.json" 就帶有 emitDecoratorMetadata: true

這意味著你有責(zé)任給所有服務(wù)類加上 @Injectable()。

依賴注入令牌

當(dāng)使用提供者配置注入器時(shí),就會(huì)把提供者和一個(gè) DI 令牌關(guān)聯(lián)起來(lái)。 注入器維護(hù)一個(gè)內(nèi)部令牌-提供者的映射表,當(dāng)請(qǐng)求一個(gè)依賴項(xiàng)時(shí)就會(huì)引用它。令牌就是這個(gè)映射表的鍵。

在簡(jiǎn)單的例子中,依賴項(xiàng)的值是一個(gè)實(shí)例,而類的類型則充當(dāng)鍵來(lái)查閱它。 通過(guò)把 HeroService 類型作為令牌,你可以直接從注入器中獲得一個(gè) HeroService 實(shí)例。

Path:"src/app/injector.component.ts" 。

heroService: HeroService;

當(dāng)你編寫的構(gòu)造函數(shù)中需要注入基于類的依賴項(xiàng)時(shí),其行為也類似。 當(dāng)你使用 HeroService 類的類型來(lái)定義構(gòu)造函數(shù)參數(shù)時(shí),Angular 就會(huì)知道要注入與 HeroService 類這個(gè)令牌相關(guān)的服務(wù)。

Path:"src/app/heroes/hero-list.component.ts" 。

constructor(heroService: HeroService)

很多依賴項(xiàng)的值都是通過(guò)類來(lái)提供的,但不是全部。擴(kuò)展的 provide 對(duì)象讓你可以把多種不同種類的提供者和 DI 令牌關(guān)聯(lián)起來(lái)。

可選依賴

HeroService 需要一個(gè)記錄器,但是如果找不到它會(huì)怎么樣?

當(dāng)組件或服務(wù)聲明某個(gè)依賴項(xiàng)時(shí),該類的構(gòu)造函數(shù)會(huì)以參數(shù)的形式接收那個(gè)依賴項(xiàng)。 通過(guò)給這個(gè)參數(shù)加上 @Optional() 注解,你可以告訴 Angular,該依賴是可選的。

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

constructor(@Optional() private logger?: Logger) {
  if (this.logger) {
    this.logger.log(some_message);
  }
}

當(dāng)使用 @Optional() 時(shí),你的代碼必須能正確處理 null 值。如果你沒(méi)有在任何地方注冊(cè)過(guò) logger 提供者,那么注入器就會(huì)把 logger 的值設(shè)置為 null。

@Inject()@Optional() 都是參數(shù)裝飾器。它們通過(guò)在需要依賴項(xiàng)的類的構(gòu)造函數(shù)上對(duì)參數(shù)進(jìn)行注解,來(lái)改變 DI 框架提供依賴項(xiàng)的方式。

小結(jié)

本節(jié)中你學(xué)到了 Angular 依賴注入的基礎(chǔ)知識(shí)。 你可以注冊(cè)多種提供者,并且知道了如何通過(guò)為構(gòu)造函數(shù)添加參數(shù)來(lái)請(qǐng)求所注入的對(duì)象(比如服務(wù))。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)