核心原理
Nest.js 的核心原理其实就是通过装饰器
给 class 或者对象添加元数据,然后初始化的时候取出这些元数据,进行依赖的分析,然后创建对应的实例对象就可以了。它的核心就是 IOC 容器,也就是自动扫描依赖,创建实例对象并且自动依赖注入。Nest 的 Controller、Module、Service
等等所有的装饰器都是通过 Reflect.meatdata
给类或对象添加元数据的,然后初始化的时候取出来做依赖的扫描,实例化后放到 IOC 容器里。
这个核心原理其实和springBoot差不多, 也是扫描装饰器 这些所谓框架的原理都是所谓的元编程
我们需要知道两个比较新的特性:
装饰器
Reflect.meatdata
这第一篇先来搞懂装饰器。
TypeScript 是这样描述装饰器的:
- 装饰器提供了一种为类声明和成员添加
注解
和元编程
语法的方法。- 装饰器是一种特殊类型的声明,可以附加到类声明、方法、访问器、属性或参数。装饰器使用 形式
@expression
,其中expression
必须求值为将在运行时调用的函数,其中包含有关装饰声明的信息。
装饰器本质
装饰器本质上是一种特殊的函数被应用在于:
- 类
- 类属性
- 类方法
- 类访问器
- 类方法的参数(在
TypeScript 5.x
中不再包含)
一句话:装饰器就是一个接收特定参数的函数,使用
@函数名
可以对一些类,属性,方法等进行装饰来实现一些 运行时 的 hook 拦截机制。
元编程
在一些装饰器的教程中,通常会遇到一个名词:元编程。什么是元编程?比较晦涩难懂的说法是:
我们不编写处理用户数据的代码(编程)。 我们编写的代码是处理用户数据的代码(元编程)。
通俗来讲:元编程 (meta-programming) 是通过操作 程序实体 (program entity),在 编译时 (compile time) 计算出 运行时 (runtime) 需要的常数、类型、代码的方法。它的诞生是源于:需要非常灵活的代码来适应快速变化的需求,同时保证性能。 与一般代码的区别是:
一般代码的操作对象是数据。 元编程的操作对象是代码:code as data。 如果编程的本质是抽象,那么元编程就是更高层次的抽象。
注意事项
需要注意的是,在 TypeScript 5.x 之前,装饰器还只是作为一个实验性功能,使用时需要在tsconfig.json
中开启以下配置:
json
复制代码
{ "compilerOptions": { "target": "ES5", "experimentalDecorators": true, "emitDecoratorMetadata": true } }
从 v5.0 开始,不需要此设置,默认情况下可以使用 stage 3 的装饰器。
- 第一个,
experimentalDecorators
打开装饰器支持。 - 第二个,
emitDecoratorMetadata
,发出包所需的数据reflect-metadata
。这个包使我们能够通过记录有关类、属性、方法和参数的元数据,在装饰器中做一些更强大的事情。
带参数的类装饰器
装饰器也可以接受参数,这需要遵循一种不同的模式,即装饰器工厂。下面是一个简单的类装饰器示例,它不仅展示了如何传递参数,还帮助我们理解了使用多个装饰器时的执行顺序。
function withParam(path: string) {
console.log(`outer withParam ${path}`);
return (target: Function) => {
console.log(`inner withParam ${path}`);
};
}
@withParam('first')
@withParam('middle')
@withParam('last')
class ExampleClass {
// ...
}
在这个示例中,我们加了三次 withParam
,打印出的信息是:
yaml
复制代码
%% 工厂函数是自上而下执行的 %%
outer withParam first
outer withParam middle
outer withParam last
%% 装饰器函数则是自下而上执行的 %%
inner withParam last
inner withParam middle
inner withParam first
记住,工厂函数是自上而下执行的,而装饰器函数则是自下而上执行的。
TMD 就不能搞成顺序都是从上往下吗,尼玛的