Angular 4 依赖注入教程之一 依赖注入简介javascript
Angular 4 依赖注入教程之六 Injectable 装饰器github
Angular 4 依赖注入教程之八 InjectToken的使用typescript
本系列教程的开发环境及开发语言:shell
Angular 4 +segmentfault
它是一个表达式
该表达式被执行后,返回一个函数
函数的入参分别为 targe、name 和 descriptor
执行该函数后,可能返回 descriptor 对象,用于配置 target 对象
类装饰器 (Class decorators)
属性装饰器 (Property decorators)
方法装饰器 (Method decorators)
参数装饰器 (Parameter decorators)
类装饰器声明:
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void
类装饰器顾名思义,就是用来装饰类的。它接收一个参数:
target: TFunction - 被装饰的类
看完第一眼后,是否是感受都很差了。没事,咱们立刻来个例子:
function Greeter(target: Function): void { target.prototype.greet = function (): void { console.log('Hello!'); } } @Greeter class Greeting { constructor() { // 内部实现 } } let myGreeting = new Greeting(); myGreeting.greet(); // console output: 'Hello!';
上面的例子中,咱们定义了 Greeter
类装饰器,同时咱们使用了 @Greeter
语法,来使用装饰器。
import { Injectable } from '@angular/core'; @Injectable() class HeroService {}
在介绍 Injectable
装饰器前,咱们先来回顾一下 HeroComponent
组件:
@Component({ selector: 'app-hero', template: ` <ul> <li *ngFor="let hero of heros"> ID: {{hero.id}} - Name: {{hero.name}} </li> </ul> ` }) export class HeroComponent implements OnInit { heros: Array<{ id: number; name: string }>; constructor(private heroService: HeroService, private loggerService: LoggerService) { } ngOnInit() { this.loggerService.log('Fetching heros...'); this.heros = this.heroService.getHeros(); } }
在 HeroComponent
组件的 ngOnInit
生命周期钩子中,咱们在获取英雄信息前输出相应的调试信息。其实为了不在每一个应用的组件中都添加 log
语句,咱们能够把 log
语句放在 getHeros()
方法内。
export class HeroService { heros: Array<{ id: number; name: string }> = [ { id: 11, name: 'Mr. Nice' }, { id: 12, name: 'Narco' }, { id: 13, name: 'Bombasto' }, { id: 14, name: 'Celeritas' }, { id: 15, name: 'Magneta' }, { id: 16, name: 'RubberMan' }, { id: 17, name: 'Dynama' }, { id: 18, name: 'Dr IQ' }, { id: 19, name: 'Magma' }, { id: 20, name: 'Tornado' } ]; getHeros() { return this.heros; } }
import { LoggerService } from './logger.service'; export class HeroService { constructor(private loggerService: LoggerService) { } heros: Array<{ id: number; name: string }> = [ { id: 11, name: 'Mr. Nice' }, { id: 12, name: 'Narco' }, { id: 13, name: 'Bombasto' }, { id: 14, name: 'Celeritas' }, { id: 15, name: 'Magneta' } ]; getHeros() { this.loggerService.log('Fetching heros...'); return this.heros; } }
当以上代码运行后会抛出如下异常信息:
Uncaught Error: Can't resolve all parameters for HeroService: (?).
上面异常信息说明没法解析 HeroService
的全部参数,而 HeroService
服务的构造函数以下:
export class HeroService { constructor(private loggerService: LoggerService) { } }
该构造函数的输入参数是 loggerService
且它的类型是 LoggerService
。在继续深刻研究以前,咱们来看一下 HeroService
最终生成的 ES5
代码:
var HeroService = (function() { function HeroService(loggerService) { this.loggerService = loggerService; this.heros = [{...}, ...]; } HeroService.prototype.getHeros = function() { this.loggerService.log('Fetching heros...'); return this.heros; }; return HeroService; }());
咱们发现生成的 ES5
代码中,HeroService
构造函数中是没有包含任何类型信息的,所以 Angular Injector (注入器) 就没法正常工做了。那么要怎么保存 HeroService
类构造函数中参数的类型信息呢?相信你已经想到了答案 — 固然是使用 Injectable
装饰器咯。接下来咱们更新一下 HeroService
:
import { Injectable } from '@angular/core'; import { LoggerService } from './logger.service'; @Injectable() export class HeroService { // ... }
更新完上面的代码,成功保存后,在 http://localhost:4200/
页面,你将看到熟悉的 "身影":
ID: 11 - Name: Mr. Nice ID: 12 - Name: Narco ID: 13 - Name: Bombasto ID: 14 - Name: Celeritas ID: 15 - Name: Magneta
如今咱们再来看一下 HeroService
类生成的 ES5
代码:
var HeroService = (function() { function HeroService(loggerService) { this.loggerService = loggerService; this.heros = [{...}, ...]; } HeroService.prototype.getHeros = function() { this.loggerService.log('Fetching heros...'); return this.heros; }; return HeroService; }()); HeroService = __decorate([__webpack_require__.i( __WEBPACK_IMPORTED_MODULE_0__angular_core__["c"/* Injectable */ ])(), __metadata("design:paramtypes", ...)], HeroService);
var __decorate = (this && this.__decorate) || function(decorators, target, key, desc) {...};
var __metadata = (this && this.__metadata) || function(k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); };
咱们发现相比未使用 Injectable
装饰器,HeroService
服务生成的 ES5
代码多出了 HeroService = __decorate(...)
这些代码。简单起见,我稍微介绍一下,经过 Injectable
装饰器,在编译时会把 HeroService
服务构造函数中参数的类型信息,经过 Reflect
API 保存在 window['__core-js_shared__']
对象的内部属性中。当 Injector
建立 HeroService
对象时,会经过 Reflect
API 去读取以前已保存的构造函数中参数的类型信息,进而正确的完成实例化操做。
有兴趣的读者,能够查看 Angular 4.x 修仙之路 中 Decorator(装饰器)
章节的相关文章。
若是所建立的服务不依赖于其余对象,是能够不用使用 Injectable
类装饰器。但当该服务须要在构造函数中注入依赖对象,就须要使用 Injectable
装饰器。不过比较推荐的作法无论是否有依赖对象,在建立服务时都使用 Injectable
类装饰器。