Angular 依赖注入
一、什么是依赖注入控制反转(IoC)
IoC强调的是对代码引用的控制权由调用方转移到了外部容器,在运行是通过某种方式注入进来,实现了控制反转,这大大降低了程序之间的耦合度。依赖注入是最常用的一种实现IoC的方式,另一种是依赖查找。 依赖注入(Dependency Injection) 当然,按照惯例我们应该举个例子, 哦对,我们主要说明的是依赖注入,依赖查找请自行查阅资料。 假设我们有一个能做汉堡的设备(HRobot),需要用肉(meat)和一些沙拉(salad)作为原料,我们可以这样实现: export class HRobot { public meat: Meat; public salad: Salad; constructor() { this.meat = new Meat(); this.salad = new Salad(); } cook() {} } 看一下好像没有什么问题,可能你已经发现,我们的原材料都是放在机器里面的,如果我们想吃别的口味的汉堡恐怕就要去乡村基了。 export class HRobot { public meat: Meat; public salad: Salad; constructor(public meat: Meat,public salad: Salad) { this.meat = meat; this.salad = salad; } cook() {} } 现在,只要要直接给它meat和salad就好了,我们的 let hRobot = new HRobot(new Meat(),new Salad()); 比如,我们想吃鸡肉汉堡,只需要个它一块鸡肉就好: class Chicken extends Meat { meat = 'chiken'; } let cRobot = new HRobot(new Chicken(),new Salad()); 感觉还不错,我们再也不会为了吃一个鸡肉汉堡大费周章的去改造一台机器,这太不可思议了。 我可能想到了,你还是懒得弄块鸡肉给它,这时候可以使用工厂函数: export class HRobotFactory { createHRobot() { let robot = new HRobot(this.createMeat(),this.createSalad()); } createMeat() { return new Meat(); } creatSalad() { return new Salad(); } } 现在有了工厂,就有源源不断的汉堡可以吃了,开不开心,惊不惊喜? 二、 Angular依赖注入在介绍Angular依赖注入之前,先来理一下三个概念:
说了半天到底是什么样的? var injector = new Injector(...); var robot = injector.get(HRobot); robot.cook();
import { ReflecttiveInjector } form '@angular/core'; var injector = ReflectiveInjector.resolveAndCreat([ {provide: HRobot,useClass: HRobot},{provide: Meat,useClass: Meat},{provide: Salad,useClass: Salad} ]); 还有注入器是这样知道知道初始化 export class Robot { //... consructor(public meat: Meat,public salad: Salad) {} //... } 当然,看了头大是应该的,因为上面的东西压根就不需要自己动手写, 1. 在组件中注入服务
例子来了: // app.component.ts //... // 1. 导入被依赖对象的服务 import { MyService } from './my-service/my-service.service'; @Component({ //... // 2. 在组件中配置注入器 providers: [ MyService ] //... }) export class AppComponent { // 3. 在构造函数中声明需要注入的依赖 constructor(private myService: MyService) {} } 2. 在服务中注入服务 // power.service.ts import { Injectable } from '@angular/core'; @Injectable() export class PowerService { // power come from here.. } // count.service.ts import { Injectable } from '@angular/core'; import { PowerService } from './power/power.service'; @Injectable() export class CountService { constructor(private power: PoowerService) {} } // app.component.ts 这里是当前组件,其实模块中的注入也一样,后面讲到 //... providers: [ CountService,PowerService ] 这里需要注意的是 3. 在模块中注入服务 // app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent,],imports: [ BrowserModule ],providers: [CountService,PowerService],bootstrap: [AppComponent] }) export class AppModule { } 与在组件中注入不同的是,在 下面说两种特殊情况:
// ... @NgModule({ imports: [ AModule,BModule ] // ... }) 那么后面导入的模块中的服务会覆盖前面导入模块中的服务,也就是说
// a.module.ts // ... @NgModule({ imports: [BModule] }) 那么这种情况下两个模块使用的都是 三、Provider1. Provider的理解 // ... @Component({ //... // 2. 在组件中配置注入器 providers: [ MyService ] //... }) 实际上它的完整形式应该是这样的: @Component({ //... // 2. 在组件中配置注入器 providers: [ {provide: MyService,useClass: MyService} ] //... }) 所以说我们上面只使用了一种 2. Provider注册方式 上面提到我只使用了其中一种注册方式,那么下面介绍
1. 类 类`Provider` 基于令牌(`Token`)指定依赖项,这种方式可是让依赖被动态指定为其他不同的具体实现,只要接口不变,对于使用方就是透明的。比如数据渲染服务(`Render`),`Render`服务对上层提供的接口是固定的,倒是底层的渲染方式可以不同: var inject = Injector.resolveAndCreate([ {provide: Render,useClass: DomRender} //{provide: Render,useClass: DomRender} // canvas 渲染方式 //{provide: Render,useClass: DomRender} // 服务的想染方式 ]) // 调用方不用做任何修改 class AppComponent { construtor(private render: Render) {} } 2. 值 由于依赖的对象并不一定都是类,也可以是字符串、常量、对象等其他数据类型的,这可以方便用在全局变量、系统相关参数配置场景中。在创建`Provider`对象的时候,只需要使用`useValue`就可以声明一个值`Provider`: let freeMan = { freeJob: boolen; live: () => {return 'do something u cant do'} }; @Component({ // ... providers: [ {provide: 'someone',useValue: freeMan} ] }) 3. 别名 有了别名`Provider`,我们就可以在一个`Provider`中配置多个令牌(`Token`),其对于的对象指向同一个实例,从而实现了多个依赖、一个对象实例的作用: // ... providers: [ {provider: Power1,useClass: PowerService},{provider: Power2,useClass: PowerService} ] // ... 仔细想想,这样对吗? // ... providers: [ {provider: Power1,useExisting: PowerService} ] // ... 4. 工厂 工厂`Provider`允许我们根据不同的条件来实例化不同的服务,比如,我们在开发环境需要打印日志,但是在实际部署的时候可能并不需要打印这些东西,那么我们总不可能去找到整个应用中所有的`console.log()`这样的方法吧,这个时候我们可以使用工厂`provider`来帮我们处理,我们只需要在工厂`provider`中设定一个条件,使其能够根据条件返回实例化我们需要的服务就可以了。为了实现这样的功能我们可以在根模块中这样注入: // app.module.ts @NgModule({ // ... providers: [ HeroService,ConsoleService,{ provide: LoggerService,useFactory: (consoleService) => { return new LoggerService(true,consoleService); },deps: [ConsoleService] } ],bootstrap: [AppComponent] }) export class AppModule { } 哦哦,那两个服务是这样写的: // console.service.ts // ... export class ConsoleService { log(message) { console.log(`ConsoleService: ${message}`); } } // logger.service.ts // ... export class LoggerService { constructor(private enable: boolean,consoleService: ConsoleService ) { } log(message: string) { if (this.enable) { console.log(`LoggerService: ${message}`); } } } 然后在组件构造函数中写上需要的服务就好。 四、限定方式的依赖注入想象一场景,你应用中的某个服务的 @Optional // app.component.ts // ... import { Optional } from '@angular/core'; constructor(@Optional() private logger: LoggerService) { if (this.logger) { this.logger.log('i am choosed'); } } 像例子中的那样只需要在宿主组件(Host Component)的构造函数中增加 @Host
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |