用户登录
用户注册

分享至

Angular 4依赖注入学习教程之Injectable装饰器(六)

  • 作者: 嗫?暁雲?
  • 来源: 51数据库
  • 2021-09-24

学习目录

  • angular 4 依赖注入教程之一 依赖注入简介
  • angular 4 依赖注入教程之二 组件服务注入
  • angular 4 依赖注入教程之三 classprovider的使用
  • angular 4 依赖注入教程之四 factoryprovider的使用
  • angular 4 依赖注入教程之五 factoryprovider配置依赖对象
  • angular 4 依赖注入教程之六 injectable 装饰器
  • angular 4 依赖注入教程之七 valueprovider的使用
  • angular 4 依赖注入教程之八 injecttoken的使用

本文主要给大家介绍的是关于angular 4依赖注入之injectable装饰器的相关内容,分享出来供大家参考学习,下面来看看详细的介绍:

本系列教程的开发环境及开发语言:

  • angular 4 +
  • angular cli
  • typescript

基础知识

装饰器是什么

  • 它是一个表达式
  • 该表达式被执行后,返回一个函数
  • 函数的入参分别为 targe、name 和 descriptor
  • 执行该函数后,可能返回 descriptor 对象,用于配置 target 对象 

装饰器的分类

  • 类装饰器 (class decorators)
  • 属性装饰器 (property decorators)
  • 方法装饰器 (method decorators)
  • 参数装饰器 (parameter decorators)

typescript 类装饰器

类装饰器声明:

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 语法,来使用装饰器。

injectable 类装饰器使用

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

@injectable()
class heroservice {}

injectable 装饰器

在介绍 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() 方法内。

更新前 heroservice 服务

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;
 }
}

更新后 heroservice 服务

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);

__decorate 函数

var __decorate = (this && this.__decorate) || function(decorators, target, key, desc) {...};

__metadata 函数

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 去读取之前已保存的构造函数中参数的类型信息,进而正确的完成实例化操作。

我有话说

@injectable() 是必须的么?

如果所创建的服务不依赖于其他对象,是可以不用使用 injectable 类装饰器。但当该服务需要在构造函数中注入依赖对象,就需要使用 injectable 装饰器。不过比较推荐的做法不管是否有依赖对象,在创建服务时都使用 injectable 类装饰器。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者使用angular 4能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。

软件
前端设计
程序设计
Java相关