Nest系列概念篇,聊聊装饰器。
装饰器
基本概念
装饰器,顾名思义就是用来装饰某件物体,在一定程度上增加了某件物体的功能,以解决更多的实际需求。
举个栗子🌰,一间房子里面放了一张床,那么就实现了这个房间可以睡觉的基本需求,在这个基础上新增了沙发、红酒杯、电视机,那么就可以实现坐在沙发上摇着红酒看综艺节目的理想状态。此时沙发、红酒杯、电视机起到了装饰器的效果,在不影响房间可以用来睡觉的前提下扩展了更多的功能,实现松耦合和易扩展。
装饰器种类
上个例子中,沙发、红酒杯、电视机属于不同的装饰器,它们分别实现了不同的功能,同样的,常用的装饰器也分为以下几种:
- 类装饰器
- 方法装饰器
- 属性装饰器
- 参数装饰器
下面分别用几个简单的例子来说明。
ES6中装饰器使用
typescript
/**
* 类装饰器
*/
const doc: ClassDecorator = (target: any) => {
target.prototype.name = 'jmin'
console.log(target);
}
@doc
class App {
constructor() {
}
}
const app: Record<string, any> = new App()
console.log('app name: ' + app.name);
/**
* 属性装饰器
*/
const prop: PropertyDecorator = (target: Object, propertyKey: string | Symbol) => {
console.log('------属性装饰器-------');
console.log(target);
console.log(propertyKey);
console.log('------属性装饰器-------');
}
class User {
@prop
name: string = 'jmin'
}
/**
* 方法装饰器
*/
const method: MethodDecorator = (target: Object, propertyKey: string | Symbol, descriptor: PropertyDescriptor) => {
console.log("---------------方法装饰器-----------------");
console.log(target);
console.log(propertyKey);
console.log(descriptor);
console.log("---------------方法装饰器-----------------");
}
class User2 {
@method
getName() {
return 'jmin';
}
}
/**
* 参数装饰器
*/
const param: ParameterDecorator = (target: Object, propertyKey: string | Symbol | undefined, index: number) => {
console.log('-------------参数装饰器------------------');
console.log(target);
console.log(propertyKey);
console.log(index);
console.log('-------------参数装饰器------------------');
}
class User3 {
getName(@param name: string) {
return name;
}
}
const user3 = new User3();
console.log(user3.getName('jmin'));
Nest中的装饰器
在nest
中实现AOP
思想的一种方式就是用装饰器
,这些装饰器分为异常过滤器(exception filter)
、管道(pipes)
、守卫(guards)
、拦截器(interceptors)
等等
扩展一个题外话,为什么nest
中要实现AOP
架构?
当一个请求过来的时候,一般会通过Controller控制器
、Services服务层
、Repository数据访问实体
链路,当我们不使用AOP
时,需要新增一些通用逻辑如(权限控制、日志统计、异常处理),就需要在每个请求逻辑中编写编写相关代码,这必然造成业务逻辑跟通用逻辑的耦合而让应用变得难以维护。
AOP
切面思想是将所有请求外面包裹一层,所有的请求都会通过这个切面去处理通用逻辑,实现了业务逻辑松耦合和易扩展。
再回来看一下Nest
中基础的装饰器:
typescript
// person.controller.ts
@Controller('person')
export class PersonController {
constructor(private readonly personService: PersonService) {}
@Post('/create-person')
create(@Body() createPersonDto: CreatePersonDto) {
return this.personService.create(createPersonDto);
}
}
上面可以看到,存在类装饰器、方法装饰器、参数装饰器 ,可以发现它们是支持接受参数或直接调用,如@Controller('person')
,但是ES6中不支持直接调用装饰器啊,那是怎么实现呢?
答案是通过装饰器工厂
,也叫柯里化
。
看下面的例子你就懂了:
typescript
const doc2 = (param: string) : ClassDecorator => {
console.log("param:", param);
return (target: any) => {
target.prototype.name = 'jmin'
console.log(target);
}
}
@doc2('person')
class App2 {
constructor() {
}
}
const app2: Record<string, any> = new App2()
console.log('app2 name: ' + app2.name);
这就实现了跟Nest
中相同的装饰器语法,实际上,Nest
中恰恰也是通过这种方式来实现的。
以Get(':id')
请求装饰器为例:
这里接受的path
参数就是:id
这串玩意~
总结
通过类比方式介绍了装饰器
本身,为了装饰某件物体才能够产生价值。
接着介绍装饰器的几种存在形态,以及ES6
的基本实现,还介绍了Nest
中装饰器的应用场景以及跟AOP
思想的融合,最后翻了一下源码看Nest
中装饰器的基本实现,一眼就看出跟ES6
中实现的区别。
但,这不是全部,要实现Nest
中的装饰器效果,还有一个很重要的概念就是元数据
和反射
,它们有什么作用呢?存在的意义又是什么?
欲知后事如何,请看下节分享!!!