阅读须知
本系列教程的开发环境及开发语言:
基础知识
装饰器是什么
- 它是一个表达式
- 该表达式被执行后,返回一个函数
- 函数的入参分别为 targe、name 和 descriptor
- 执行该函数后,可能返回 descriptor 对象,用于配置 target 对象
装饰器的分类
- 类装饰器 (Class decorators)
- 属性装饰器 (Property decorators)
- 方法装饰器 (Method decorators)
- 参数装饰器 (Parameter decorators)
TypeScript 类装饰器
类装饰器声明:
------- ---- -------------- - ---------- ---------- -------------------- ---------- -- --------- - ----
类装饰器顾名思义,就是用来装饰类的。它接收一个参数:
- target: TFunction - 被装饰的类
看完第一眼后,是不是感觉都不好了。没事,我们马上来个例子:
-------- --------------- ---------- ---- - ---------------------- - -------- --- ---- - ---------------------- - - -------- ----- -------- - ------------- - -- ---- - - --- ---------- - --- ----------- ------------------- -- ------- ------- ---------
上面的例子中,我们定义了 Greeter
类装饰器,同时我们使用了 @Greeter
语法,来使用装饰器。
Injectable 类装饰器使用
------ - ---------- - ---- ---------------- ------------- ----- ----------- --
Injectable 装饰器
在介绍 Injectable
装饰器前,我们先来回顾一下 HeroComponent
组件:
------------ --------- ----------- --------- - ---- --- ----------- ---- -- ------- --- ----------- - ----- ------------- ----- ----- - -- ------ ----- ------------- ---------- ------ - ------ ------- ------ ---------- -------- --------- ------ ------------------- ------------ ------------ ------- -------------- -------------- - - ---------- - -------------------------------- ----------- ---------- - ---------------------------- - -
</{>
在 HeroComponent
组件的 ngOnInit
生命周期钩子中,我们在获取英雄信息前输出相应的调试信息。其实为了避免在每个应用的组件中都添加 log
语句,我们可以把 log
语句放在 getHeros()
方法内。
更新前 HeroService 服务
------ ----- ----------- - ------ ------- ------ ---------- -------- --------- ----- - - - --- --- ----- ---- ----- -- - --- --- ----- ------- -- - --- --- ----- ---------- -- - --- --- ----- ----------- -- - --- --- ----- --------- -- - --- --- ----- ----------- -- - --- --- ----- -------- -- - --- --- ----- --- --- -- - --- --- ----- ------- -- - --- --- ----- --------- - -- ---------- - ------ ----------- - -
</{>
更新后 HeroService 服务
------ - ------------- - ---- ------------------- ------ ----- ----------- - ------------------- -------------- -------------- - - ------ ------- ------ ---------- -------- --------- ----- - - - --- --- ----- ---- ----- -- - --- --- ----- ------- -- - --- --- ----- ---------- -- - --- --- ----- ----------- -- - --- --- ----- --------- - -- ---------- - -------------------------------- ----------- ------ ----------- - -
</{>
当以上代码运行后会抛出以下异常信息:
-------- ------ ----- ------- --- ---------- --- ------------ ----
上面异常信息说明无法解析 HeroService
的所有参数,而 HeroService
服务的构造函数如下:
------ ----- ----------- - ------------------- -------------- -------------- - - -
该构造函数的输入参数是 loggerService
且它的类型是 LoggerService
。在继续深入研究之前,我们来看一下 HeroService
最终生成的 ES5
代码:
--- ----------- - ----------- - -------- -------------------------- - ------------------ - -------------- ---------- - ------- ----- - ------------------------------ - ---------- - -------------------------------- ----------- ------ ----------- -- ------ ------------ -----
我们发现生成的 ES5
代码中,HeroService
构造函数中是没有包含任何类型信息的,因此 Angular Injector (注入器) 就无法正常工作了。那么要怎么保存 HeroService
类构造函数中参数的类型信息呢?相信你已经想到了答案 — 当然是使用 Injectable
装饰器咯。接下来我们更新一下 HeroService
:
------ - ---------- - ---- ---------------- ------ - ------------- - ---- ------------------- ------------- ------ ----- ----------- - -- --- -
更新完上面的代码,成功保存后,在 http://localhost:4200/
页面,你将看到熟悉的 "身影":
--- -- - ----- --- ---- --- -- - ----- ----- --- -- - ----- -------- --- -- - ----- --------- --- -- - ----- -------
现在我们再来看一下 HeroService
类生成的 ES5
代码:
--- ----------- - ----------- - -------- -------------------------- - ------------------ - -------------- ---------- - ------- ----- - ------------------------------ - ---------- - -------------------------------- ----------- ------ ----------- -- ------ ------------ ----- ----------- - ---------------------------------- ------------------------------------------------- ---------- -- ----- ------------------------------- ------ -------------
__decorate 函数
--- ---------- - ----- -- ---------------- -- -------------------- ------- ---- ----- ------
__metadata 函数
--- ---------- - ----- -- ---------------- -- ----------- -- - -- ---- ------- --- -------- -- --- ---------------- --- ----------- ------ ------------------- --- --
我们发现相比未使用 Injectable
装饰器,HeroService
服务生成的 ES5
代码多出了 HeroService = __decorate(...)
这些代码。简单起见,我稍微介绍一下,通过 Injectable
装饰器,在编译时会把 HeroService
服务构造函数中参数的类型信息,通过 Reflect
API 保存在 window['__core-js_shared__']
对象的内部属性中。当 Injector
创建 HeroService
对象时,会通过 Reflect
API 去读取之前已保存的构造函数中参数的类型信息,进而正确的完成实例化操作。
有兴趣的读者,可以查看 Angular 4.x 修仙之路 中 Decorator(装饰器)
章节的相关文章。
我有话说
@Injectable() 是必须的么?
如果所创建的服务不依赖于其他对象,是可以不用使用 Injectable
类装饰器。但当该服务需要在构造函数中注入依赖对象,就需要使用 Injectable
装饰器。不过比较推荐的做法不管是否有依赖对象,在创建服务时都使用 Injectable
类装饰器。