You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
多个装饰器组合
装饰器也是支持多个一起使用的,还是上面 color 例子,添加多个 不同的color的装饰器
@addColor("blue")
@addColor("red")
@addColor("yellow")classPeople{}// log: run blue// log: run red// log: run yellownewPeople().colorList;// ['yellow','red','blue']
从上面的信息,可以知道。
装饰器定义的执行顺序是从上到下
装饰器运行时装饰 class 的顺序是从下到上
装饰器的基本用法
类装饰器 (Class Decorators)
类装饰器作用于类的构造函数,可用于修改或者替换一个 class 定义
一个装饰器函数签名如下:
typedecorator=(target: Function)=>Function|void;
它接收被装饰的 class 作为target函数的参数,如果装饰器函数有返回值,则使用这个返回值作为新的 class。
If you'd like to set type property of each prop value from its type definition, you can use reflect-metadata.
Set emitDecoratorMetadata to true.
Import reflect-metadata before importing vue-property-decorator (importing reflect-metadata is needed just once.)
import{Container,Service}from'typedi';
@Service()classExampleInjectedService{printMessage(){console.log('I am alive!');}}
@Service()classExampleService{constructor(// because we annotated ExampleInjectedService with the @Service()// decorator TypeDI will automatically inject an instance of// ExampleInjectedService here when the ExampleService class is requested// from TypeDI.privateinjectedService: ExampleInjectedService){}}constserviceInstance=Container.get(ExampleService);// we request an instance of ExampleService from TypeDIserviceInstance.injectedService.printMessage();// logs "I am alive!" to the console
为什么要用装饰器
引入装饰器更能够便于代码逻辑的解藕和复用。举一个例子
举一个非常常见的需求。假设我们有一个类
Network
,它有一个异步getList
方法有一天,我们想给它加个全局
loading
,那么我们可能会这么写如果需要对另一个方法使用全局 loading,可能又需要再写一遍。并且这个代码还入侵了函数本身的逻辑。这时候使用装饰器就可以相对优雅解决这个问题。
实现一个
loadingDecorator
装饰器使用我们的装饰器
这样,每当一个方法需要加 loading 的时候,给它使用
@loadingDecorator
装饰器即可。这样即逻辑解藕又能实现比较好的代码复用经过
typescript
转码后的代码长这样,感兴趣的同学可以看看什么是装饰器
装饰器是一种特殊的声明,可以作用在类的声明、方法、属性、访问器或者参数上。装饰器的用法是
@decorator
。decorator
是一个函数,会在运行时的时候调用,对类进行一些修改。需要注意的是,在javascript
中,装饰器只能用于类,不能作用于普通函数。原因是函数会存在函数提升,设计者为了减少一些复杂性,可以参照一个讨论如下就是定义一个装饰器函数,并且作用在类上
其实就是类似于以下代码
装饰器本质就是一个函数,所以也可以利用闭包的能力实现更多功能。装饰器工厂就是一个返回函数的函数,运行时将会被调用
装饰器也是支持多个一起使用的,还是上面 color 例子,添加多个 不同的color的装饰器
从上面的信息,可以知道。
装饰器的基本用法
类装饰器 (Class Decorators)
类装饰器作用于类的构造函数,可用于修改或者替换一个 class 定义
一个装饰器函数签名如下:
它接收被装饰的 class 作为
target
函数的参数,如果装饰器函数有返回值,则使用这个返回值作为新的 class。当然,上面有返回值的形式直接返回也行。
类成员装饰器
下面列举的几个都是装饰到类的成员上,所以都可以归为一类
属性装饰器 (Property Decorators)
属性装饰器用于装饰属性,函数签名如下
属性装饰器的参数定义如下:
1、第一个参数。如果装饰的是静态方法,则是这个类 Target 本身;如果装饰的是原型方法,则是类的原型对象 Target.prototype
2、第二个参数。这个属性的名称
属性装饰器的返回值是被忽略的,所以如果需要修改属性值。分两种情况
Object.getOwnPropertyDescriptor(target, propertyKey)
和Object.defineProperty(target,propertyKey,{})
来获取和修改descriptor
但这样的装饰器也不是没有作用,在 typescript 中可以很方便的收集元类型信息,后面的文章会说到
方法装饰器 (Method Decorators)
方法装饰器就是用来装饰方法,可以用来修改方法的定义。方法装饰器的函数签名如下
方法装饰器的参数定义如下:
1、第一个参数。如果装饰的是静态方法,则是这个类
Target
本身;如果装饰的是原型方法,则是类的原型对象Target.prototype
2、第二个参数。这个方法的名称
3、第三个参数,这个方法的属性描述符,通过
descriptor.value
可以直接拿到这个方法如果属性装饰器有返回值,这个返回值讲作为这个方法的属性描述符。对象的属性描述符就是调用
Reflect.getOwnPropertyDescriptor(target, propertyKey)
的返回值,详细可见访问器装饰器 (Accessor Decorators)
参数装饰器 (Parameter Decorators)
参数装饰器的函数签名如下
参数装饰器的参数定义如下:
1、第一个参数。如果装饰的是静态方法的参数,则是这个类
Target
本身;如果装饰的是原型方法的参数,则是类的原型对象Target.prototype
2、第二个参数。参数所处的函数名称
3、第三个参数,该参数位于函数参数列表的位置下标(number)
各种装饰器的执行顺序
如下:
1、先执行实例成员装饰器(非静态的),再执行静态成员装饰器
2、执行成员的装饰器时,先执行参数装饰器,再执行作用于成员的装饰器
3、执行完 1、2 后,执行构造函数的参数装饰器;最后执行作用于 class 的装饰器
typescript 更强大的装饰器
在
vue-property-decorator
中的应用上面提到的一些用法更多是 javascript 场景中使用装饰器优化我们代码的结构,在
typescript
中,装饰器还有有一个更强大的功能,就是能在运行时去拿到我们在typescript
定义的时候类型信息。如果用过
typescript
写vue
的同学,一般会用到vue-decorator-property
这个库。在Prop
我们可以看到文档这样写我们就不需要去在
Prop
的options
的 type 再去定义一遍这个属性告诉 vue 了。这个能力正是typescript
的emitDecoratorMetadata
特性提供的。我们看上面的代码经过 ts 编译后的效果如下,地址可见我们的类型信息被收集到 metadata 的
design:type
中,通过reflect-metadata
提供的一些方法我们就能在运行时拿到这个类型信息。可以理解为将每个被装饰的类/属性/方法的类型存放到一个全局的地方,key 为
design:type
。后续处理的时候可以通过class
/method
/key
拿到这个类型信息,做一些我们想做的事情。在 node 中的应用
如果我们想基于 class 声明编写 http 接口,而不是写很多
router.get
/router.post
这样写法。例如如下:很显然,这里我们是定义了两个接口,分别是
/test/a
和test/b
。这里的关键就在于实现Controller
和Post
/Get
装饰器Controller
作用于 class 上,我们定义一个元信息key
并使用Reflect.defineMetadata
存对应的元信息再实现一个工厂装饰器,返回
Get
/Post
createMappingDecorator
接收一个参数(表示这是Get
还是Post
),返回一个装饰器。装饰器调用defineMetadata
存了PATH_METADATA
和METHOD_METADATA
两个 key,value 分别是请求路径和方法。所以综上装饰后,可以类比一个以下形式的存储结构
取值并映射函数生成route
最后,只需把 route 相关信息绑在对应的http框架上即可
reflect-metadata
更多api可以参考typedi
最后再简单介绍介绍
typedi
typedi是一个 typescript(javascript)的依赖注入工具,可以在 node.js 和浏览器中构造易于测试和良好架构的应用程序。主要有以下特性:
container
官网例子,非常方便实现依赖注入使用
最后
码字不易,一键三连的人明年会有好运哦,祝大家新年快乐!!!
参考资料
typescript Decorators
深入理解 typescript
The text was updated successfully, but these errors were encountered: