成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Angular2 Dependency Injection

yhaolpz / 837人閱讀

摘要:前言依賴注入是的核心概念之一。會(huì)幫我們管理并且維護(hù)這些依賴關(guān)系。在組件當(dāng)中我們沒(méi)有看到任何操作符,但是程序啟動(dòng)后我們可以看到控制臺(tái)打印了。

前言

依賴注入是Angular的核心概念之一。通過(guò)依賴注入,我們可以將復(fù)雜、繁瑣的對(duì)象管理工作交給Angular,將我們的工作重心更好的放在業(yè)務(wù)上。
依賴注入本身是后端編碼的概念,熟悉Spring框架的對(duì)其應(yīng)該不陌生,Angular1首次將依賴注入引入前端開發(fā),Angular2繼續(xù)將其發(fā)揚(yáng)光大,同時(shí)又很好的解決了Angular1中依賴注入所遺留的問(wèn)題和瓶頸。
那么什么是依賴注入呢?我覺(jué)得可以分為兩個(gè)方面去解讀

依賴注入是一種設(shè)計(jì)模式

面向?qū)ο缶幊?,我們以類為單位組織我們的代碼。舉個(gè)簡(jiǎn)單的例子,例如某一款汽車,有引擎、輪胎、車門等配置,抽象成代碼就是這樣的

class Car {
  constructor() {
    this.engine = new Engine();
    this.tires = new Tires();
    this.doors = new Doors();
  }
}

在構(gòu)造汽車的過(guò)程中,我們安裝引擎、輪胎和車門等配置,這樣就可以造出一輛汽車了。但是現(xiàn)在我們還想造同一款車,但是想換一種引擎,怎么辦?很明顯,上面的Car是整個(gè)封閉的,如果想換一個(gè)引擎,我們就得重新造一款車

class OtherCar {
  constructor() {
    this.engine = new OtherEngine();
    this.tires = new Tires();
    this.doors = new Doors();
  }
}

相信大家已經(jīng)發(fā)現(xiàn)上面代碼的問(wèn)題了,耦合性太強(qiáng),無(wú)法定制我們的引擎、輪胎和車門,要想定制,就得從頭來(lái)過(guò)。如果我們的所有的引擎都符合某一個(gè)標(biāo)準(zhǔn)尺寸,然后在車?yán)镱A(yù)留出這個(gè)空間,那么我們不就可以隨意更換引擎了么?同理輪胎和車門,抽象成代碼就是這樣的

class Car {
  constructor(engine, tires, doors) {
    this.engine = engine;
    this.tires = tires;
    this.doors = doors;
  }
}

通過(guò)組裝的方式造車,預(yù)留配置的標(biāo)準(zhǔn)空間,同一款車我們可以隨意使用各種配置

var car = new Car(
  new Engine(),
  new Tires(),
  new Doors()
);
var car = new Car(
  new MockEngine(),
  new MockTires(),
  new MockDoors()
);

從測(cè)試的角度來(lái)說(shuō),這樣的代碼也是方便測(cè)試的。上面的注入方式就是構(gòu)造器注入,通過(guò)這樣的一種模式可以使我們的代碼更加健壯同時(shí)也是易于測(cè)試的。
但是上面注入的實(shí)例都是我們手動(dòng)去new的,當(dāng)應(yīng)用越來(lái)越大的時(shí)候,我們的依賴會(huì)更復(fù)雜,試著想一下某個(gè)類依賴于十幾個(gè)類,而這些類之間又相互依賴,管理這些依賴關(guān)系就是件讓人頭疼的事情了。
Angular會(huì)幫我們管理并且維護(hù)這些依賴關(guān)系。

依賴注入是一種框架

Angular1中我們可以使用service注入服務(wù),就像這樣

angular.module("app", [])
        .controller("MyCtrl", function ($scope, comService) {
            comService.handle();
        })
        .service("comService", function () {
            this.handle = function () {
                //todo
            }
        });

但是Angular1的依賴注入有幾個(gè)問(wèn)題

所有的服務(wù)全部都是單例的

var id = 1;
angular.module("app", [])
        .service("comService", function () {
            this._id = id++;

            this.getId = function () {
                return this._id;
            }
        })
        .controller("ACtrl", function ($scope, comService) {
            console.log(comService.getId()); // 1
        })
        .controller("BCtrl", function ($scope, comService) {
            console.log(comService.getId()); // 1
        });

服務(wù)是通過(guò)名稱來(lái)區(qū)分的,很容易造成沖突,后者會(huì)直接覆蓋前者

angular.module("app", [])
        .service("comService", function () {
            this.name = "company service 1";
        })
        .service("comService", function () {
            this.name = "company service 2";
        })
        .controller("ACtrl", function ($scope, comService) {
            console.log(comService.name); // company service 2
        });

依賴注入功能內(nèi)嵌在Angular1中,無(wú)法剝離出來(lái)多帶帶使用

Angular2中的依賴注入 組件注入服務(wù)

例如有一個(gè)日志服務(wù)logger.service.ts

export default class LoggerService {
    log(str) {
        console.log(`Log: ${str}`);
    }
}

然后入口組件app.ts當(dāng)中使用這個(gè)服務(wù)

import {Component}   from "angular2/core";
import LoggerService from "./logger.service";

@Component({
    selector: "my-app",
    template: "

App Component

", providers:[LoggerService] }) export class AppComponent { loggerService:LoggerService; constructor(loggerService:LoggerService) { this.loggerService = loggerService; } ngOnInit(){ this.loggerService.log("component init"); } }

首先我們需要在組件的providers配置中引入這個(gè)服務(wù),這點(diǎn)很重要,在Angular2的任何組件(指令等等)當(dāng)中想要使用我們自定義的服務(wù)或者其它功能必須先作出聲明。
在App組件當(dāng)中我們沒(méi)有看到任何new操作符,但是程序啟動(dòng)后我們可以看到控制臺(tái)打印了Log: component init。Angular2幫我們實(shí)例化了LoggerService并注入到了loggerService屬性當(dāng)中。
上面的代碼還可以簡(jiǎn)寫成這樣

@Component({
    selector: "my-app",
    template: "

App Component

", providers:[LoggerService] }) export class AppComponent { constructor(private loggerService:LoggerService) {} ngOnInit(){ this.loggerService.log("component init"); } }

loggerService:LoggerService,后面指定的類型必不可少,這是注入的關(guān)鍵

在Angular2組件當(dāng)中使用依賴注入可以簡(jiǎn)單的分為兩步

組件當(dāng)中作出聲明

組件構(gòu)造函數(shù)當(dāng)中注入

子組件注入服務(wù)

新建一個(gè)uuid.ts的服務(wù),可以生成一個(gè)唯一的ID

var id = 1;
export default class UuidService {
    id:number;

    constructor() {
        this.id = id++;
    }

    getId() {
        return this.id;
    }
}

入口組件app.ts

import {Component}    from "angular2/core";
import UuidService    from "./uuid.service";
import ChildComponent from "./child";

@Component({
    selector: "my-app",
    template: "

App Component

", providers:[UuidService], directives:[ChildComponent] }) export class AppComponent { constructor(private uuidService:UuidService) {} ngOnInit(){ console.log(this.uuidService.getId()); } }

新建一個(gè)子組件child.ts

import {Component}   from "angular2/core";
import UuidService from "./uuid.service";

@Component({
    selector: "my-child",
    template: "

Child Component

" }) export default class ChildComponent { constructor(private uuidService:UuidService) {} ngOnInit(){ console.log(this.uuidService.getId()) } }

在子組件當(dāng)中我們并沒(méi)有配置providers,為啥程序依然正常執(zhí)行呢?因?yàn)樽咏M件可以注入父組件聲明的服務(wù)。打開控制臺(tái)看到輸出了兩個(gè)1,說(shuō)明父子組件注入的是同一個(gè)實(shí)例,這并不符合uuid的功能,怎么辦?
我們把子組件當(dāng)中的providers聲明加上

import {Component}   from "angular2/core";
import UuidService from "./uuid.service";

@Component({
    selector: "my-child",
    template: "

Child Component

", providers:[UuidService] }) export default class ChildComponent { constructor(private uuidService:UuidService) {} ngOnInit(){ console.log(this.uuidService.getId()) } }

打開控制臺(tái),發(fā)現(xiàn)打印了1 2,這是為什么呢?Angular2當(dāng)中每個(gè)組件都有自己的依賴注入管理,依賴注入的時(shí)候會(huì)先在當(dāng)前組件上尋找服務(wù)實(shí)例,如果找不到就會(huì)使用父組件上依賴注入的實(shí)例,如果還找不到,就會(huì)拋出異常。
組件是一個(gè)樹狀結(jié)構(gòu),我們也可以把依賴注入看成和組件平行的樹狀結(jié)構(gòu),每個(gè)組件都有自己的依賴管理,這樣就解決了Angular1當(dāng)中服務(wù)單例的的問(wèn)題。

服務(wù)注入服務(wù)

有時(shí)候服務(wù)之間也會(huì)相互依賴,例如上面的例子當(dāng)中LoggerService依賴另一個(gè)FormatService

format.service.ts

export default class FormatService {
    format() {
        return "Log: ";
    }
}

logger.service.ts

import FormatService from "./format.service";

export default class LoggerService {
    constructor(private formatService:FormatService) {
    }

    log(str) {
        console.log(`${this.formatService.format()}${str}`);
    }
}

app.ts

import {Component}   from "angular2/core";
import LoggerService from "./logger.service";
import FormatService from "./format.service";

@Component({
    selector: "my-app",
    template: "

App Component

", providers: [LoggerService, FormatService] }) export class AppComponent { constructor(private loggerService:LoggerService) { } ngOnInit() { this.loggerService.log("component init"); } }

服務(wù)依賴的服務(wù)也要在providers中作出聲明

打開控制臺(tái),發(fā)現(xiàn)拋出了異常,因?yàn)槲覀儧](méi)有告知Angular2,LoggerService依賴FormatService,所以注入失敗了。
通過(guò)給LoggerService添加@Injectable()裝飾器,告知Angular2本服務(wù)需要注入其它服務(wù)

logger.service.ts

import FormatService from "./format.service";
import {Injectable} from "angular2/core";

@Injectable()
export default class LoggerService {
    constructor(private formatService:FormatService) {
    }

    log(str) {
        console.log(`${this.formatService.format()}${str}`);
    }
}

這樣我們的程序又能正常工作了。細(xì)心的同學(xué)會(huì)發(fā)現(xiàn)我們的App組件也需要注入LoggerService服務(wù),為什么不需要添加@Injectable()裝飾器?
因?yàn)榻M件聲明已經(jīng)添加了@Component()裝飾器,所以無(wú)需再次添加其它聲明了。

建議我們所有的服務(wù)都添加上@Injectable()

循環(huán)依賴注入

我們將上面的代碼改造成下面這樣

format.service.ts

import LoggerService from "./logger.service";
import {Injectable} from "angular2/core";

@Injectable()
export default class FormatService {
    constructor(private loggerService:LoggerService){}
    format() {
        return "Log: ";
    }
}

logger.service.ts

import FormatService from "./format.service";
import {Injectable} from "angular2/core";

@Injectable()
export default class LoggerService {
    constructor(private formatService:FormatService) {
    }

    log(str) {
        console.log(`${this.formatService.format()}${str}`);
    }
}

打開控制臺(tái)會(huì)發(fā)現(xiàn)拋出了異常,像這種兩個(gè)服務(wù)之間相互注入的情況就會(huì)產(chǎn)生循環(huán)依賴,我們要盡量避免這種情況的發(fā)生,保持每個(gè)服務(wù)的單一職責(zé)功能。

依賴注入核心

Angular2的依賴注入主要由三個(gè)部分構(gòu)成

Injector - 暴露接口創(chuàng)建服務(wù)實(shí)例

Provider - 包含了當(dāng)前服務(wù)的信息和依賴信息

Dependency - 服務(wù)的依賴信息

通過(guò)Injector的功能,我們可以脫離Angular2組件來(lái)使用依賴注入,例如上面的Car例子,首先引入

import {Injector, Injectable} from "angular2/core";

創(chuàng)建我們的Engine等類和Car類

class Engine{}
class Tires{}
class Doors{}

@Injectable()
class Car{
    constructor(private engine:Engine, private tires:Tires, private dorrs:Doors){}
}

Car當(dāng)中需要注入別的類,不要忘了添加 @Injectable()

調(diào)用Injector的resolveAndCreate靜態(tài)方法創(chuàng)建注入器

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

要將所有相關(guān)的類添加到參數(shù)數(shù)組中,如果實(shí)例化了參數(shù)數(shù)組中不存在的類,就會(huì)拋出異常

調(diào)用get方法獲取Car類的實(shí)例

var car = injector.get(Car);

比較下面的例子

injector.get(Tires) === injector.get(Tires); //true
car.engine === injecotr.get(Engine);     //true

同一個(gè)注入器上獲取的實(shí)例都是單例的

Token

我們知道Angular1當(dāng)中注入的識(shí)別是通過(guò)參數(shù)的字符名稱,例如

angular.module("app", [])
        .service("comService", function () {
        })
        .controller("ACtrl", function (comService) {

        });

controller當(dāng)中使用的service名稱必須和注冊(cè)處保持一致,否則注入失敗。Angular2獲取實(shí)例則是通過(guò)Token

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

這種方式實(shí)際上是簡(jiǎn)寫的,Angular2會(huì)幫我們封裝成下面的形式

var injecotr = Injector.resolveAndCreate([provide(Engine,{useClass:Engine})]);

provide是Angular2的核心方法之一,返回值是一個(gè)Provider實(shí)例。第一個(gè)參數(shù)就是Token,這里我們直接使用了類Engine作為Token,useClass表示通過(guò)實(shí)例化類的方式注入。
實(shí)際上Token可以換成別的類型,例如

var injector = Injector.resolveAndCreate([provide("engine", {useClass: Engine})]);
var engine = injector.get("engine");
console.log(engine instanceof Engine); //true

當(dāng)然了使用字符串這種方式容易被覆蓋

useClass

實(shí)例化類的方式注入,注入器會(huì)幫我們new實(shí)例,如果傳遞一個(gè)非類,typescript編譯都通不過(guò)

useValue

直接注入這個(gè)值

var injector = Injector.resolveAndCreate([
    provide(Engine, {useValue: "engine"})
]);
console.log(injector.get(Engine) === "engine"); //true
useFactory

注入工廠方法的返回值

var injector = Injector.resolveAndCreate([provide(Engine, {
    useFactory: function () {
        return "engine"
    }
})]);
console.log(injector.get(Engine) === "engine");

factory方法當(dāng)中可以依賴別的服務(wù)

var injector = Injector.resolveAndCreate([EngineA, EngineB, provide(Engine, {
    useFactory: function (engineA, engineB) {
        if (true) {
            return engineA;
        } else {
            return engineB;
        }
    },
    deps: [EngineA, EngineB]
})]);
console.log(injector.get(Engine) instanceof EngineA); //true
useExisting

使用已存在的實(shí)例注入,這個(gè)容易跟useClass弄混,注意下面的輸出

var injector = Injector.resolveAndCreate([
    EngineA,
    provide(EngineB, {useClass: EngineA})
]);
console.log(injector.get(EngineA) === injector.get(EngineB)); //false

var injector = Injector.resolveAndCreate([
    EngineA,
    provide(EngineB, {useExisting: EngineA})
]);
console.log(injector.get(EngineA) === injector.get(EngineB)); //true
multi

如果我們重復(fù)注冊(cè)同一個(gè)Token,后面的會(huì)覆蓋前面的,例如

var injector = Injector.resolveAndCreate([
    provide("COM_ID", {useValue: 1}),
    provide("COM_ID", {useValue: 2})
]);
console.log(injector.get("COM_ID")); // 2

使用multi配置可以使相同的Token共存,注入的是一個(gè)數(shù)組

var injector = Injector.resolveAndCreate([
    provide("COM_ID", {
        useValue: 1,
        multi: true
    }),
    provide("COM_ID", {
        useValue: 2,
        multi: true
    })
]);
console.log(injector.get("COM_ID")); // [1,2]

相同的Token,不能出現(xiàn)混合的情況,例如下面的寫法就會(huì)報(bào)錯(cuò)

var injector = Injector.resolveAndCreate([
    provide("COM_ID", {useValue: 1, multi: true}),
    provide("COM_ID", {useValue: 2})
]);
子注入器

通過(guò)resolveAndCreateChild可以創(chuàng)建子注入器

var injector = Injector.resolveAndCreate([Engine, Tires, Doors, Car]);
var childInjector = injector.resolveAndCreateChild([Engine, Car]);
var grantInjector = childInjector.resolveAndCreateChild([Car]);

grantInjector.get(Car) === childInjector.get(Car);       //false
grantInjector.get(Car) === injector.get(Car);            //false

grantInjector.get(Engine) === childInjector.get(Engine); //true
childInjector.get(Engine) === injector.get(Engine);      //false

grantInjector.get(Tires) === childInjector.get(Tires);   //true
childInjector.get(Tires) === injector.get(Tires);        //true

每個(gè)注入器都會(huì)有自己的依賴注入管理,它會(huì)先從本身查找服務(wù),如果找不到就會(huì)往父級(jí)注入器查找

小結(jié)

自此Angular2解決了Angular1遺留的問(wèn)題

我們可以多帶帶使用依賴注入功能

Token防止重名覆蓋

樹狀的注入器各自管理自己的實(shí)例

原文

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/79236.html

相關(guān)文章

  • angular2初入眼簾之-service

    摘要:前集回顧上一章里我們?cè)诶锿ㄟ^(guò)組合三個(gè)組件,并通過(guò)單向數(shù)據(jù)流的方式把她們驅(qū)動(dòng)起來(lái)。設(shè)計(jì)每章都會(huì)提一下,先設(shè)計(jì)使用場(chǎng)景這種方式,我們稱之為,不了解的朋友參考以手寫依賴注入。 前集回顧 上一章里我們?cè)贏ppComponent里通過(guò)組合InputItem、 CheckableItem、 Counter三個(gè)組件,并通過(guò)Unidirectional Data Flow(單向數(shù)據(jù)流)的方式把她們驅(qū)動(dòng)...

    jokester 評(píng)論0 收藏0
  • angular2初入眼簾之-了解component

    摘要:通過(guò)增加刪除元素改變布局的。譬如和控制元素顯示隱藏,或者改變?cè)匦袨榈?。譬如設(shè)計(jì)看過(guò)我之前介紹以手寫依賴注入的朋友應(yīng)該已經(jīng)對(duì)行為驅(qū)動(dòng)多少有些了解了。她有,并且包含了至少一個(gè)和一個(gè)標(biāo)簽。,將左邊的事件傳遞給了右邊的表達(dá)式通常就是事件處理函數(shù)。 前集回顧 在上一章里我們講了如何為angular2搭建開發(fā)環(huán)境(還沒(méi)搭起來(lái)的趕緊去看哦),并使之跑起來(lái)我們的第一個(gè)My First Angular...

    ixlei 評(píng)論0 收藏0
  • Laravel Dependency Injection (依賴注入) 概念詳解

    摘要:依賴注入并不限于構(gòu)造函數(shù)作為經(jīng)驗(yàn),注入最適合必須的依賴關(guān)系,比如示例中的情況注入最適合可選依賴關(guān)系,比如緩存一個(gè)對(duì)象實(shí)例。 本文翻譯自 Symfony 作者 Fabien Potencier 的 《Dependency Injection in general and the implementation of a Dependency Injection Container in P...

    Fundebug 評(píng)論0 收藏0
  • Laravel Container (容器) 概念詳解 (上)

    摘要:上文書,創(chuàng)建對(duì)象需要先創(chuàng)建對(duì)象。創(chuàng)建對(duì)象的雜活是嵌入在中的。對(duì)象使用來(lái)管理依賴關(guān)系非常好,但不是必須的。很容易實(shí)現(xiàn),但手工維護(hù)各種亂七八糟的對(duì)象還是很麻煩。所有文章均已收錄至項(xiàng)目。 本文翻譯自 Symfony 作者 Fabien Potencier 的 《Dependency Injection in general and the implementation of a Depend...

    FullStackDeveloper 評(píng)論0 收藏0
  • PHP程序員如何理解依賴注入容器(dependency injection container)

    摘要:代碼這就是控制反轉(zhuǎn)模式。是變量有默認(rèn)值則設(shè)置默認(rèn)值是一個(gè)類,遞歸解析有默認(rèn)值則返回默認(rèn)值從容器中取得以上代碼的原理參考官方文檔反射,具有完整的反射,添加了對(duì)類接口函數(shù)方法和擴(kuò)展進(jìn)行反向工程的能力。 PHP程序員如何理解依賴注入容器(dependency injection container) 背景知識(shí) 傳統(tǒng)的思路是應(yīng)用程序用到一個(gè)Foo類,就會(huì)創(chuàng)建Foo類并調(diào)用Foo類的方法,假如這...

    Coding01 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<