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
類裝飾器。