【Nest全栈之旅】第四章:对Metadata和Reflect一脸懵逼?

第二章我们讲到了装饰器的相关概念,也知道通过装饰器工厂可以扩展装饰器的功能,除此之外,想要实现Nest装饰器效果,还需要了解一些前置知识,即metadataReflect

Reflect对象

很多同学对于这个新的内置对象都熟悉,不就是用来操作对象的属性和方法吗,诸如:

js 复制代码
// set操作
const obj = {}
Reflect.set(obj , 'a', 1)
Reflect.set(obj , 'b', 2)

// get操作
Reflect.get(obj, 'a') // 1

// 判断是否存在某个元素
Reflect.has(obj, 'a') // true
Reflect.has(obj, 'c') // false

除此之外,还有Reflect.definePropertyReflect.ownKeys等在Object对象上能找到的方法。

而这里有同学可能会问,为什么已经有了操作Object的方法,还要有Reflect呢?我补充一下关于Reflect的设计初衷:

  • 为了Proxy准备,Object上有超过20种方法,但是用来操作Proxy显得太杂了,需要专门有个对象来做这件事情。让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。
  • 为了减少命令式操作Object,如delete obj.aname in obj,将这些操作变为函数行为,统一ES的编码风格
  • 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false

metadata元数据

什么是metadata元数据呢?

简单理解就是用数据来描述数据,什么意思?

举个例子:比如我有一个方法叫做getData,它被挂在methods对象上, 用元数据来描述它可以这样表示。

typescript 复制代码
metadata = {
    // 属性类型
    type: Function,
    // 返回值类型
    returnType: Number,
    // 参数类型
    paramsType: [String, Number]
}

Reflect的api我们都可以在MDN上找到,但是诸如Reflect.getMetadata怎么没有呢?

是因为metadata这个api还在草案阶段,还没有纳入es标准。 rbuckton.github.io/reflect-met...

在这里,我们就可以到看Nest相关api了

给类以及类方法添加元数据:

我们在Nest中测试一下:

PersonService类添加元数据,在Nest启动的时候就可以拿到这个数据

PersonService类中的一个方法findOne添加/获取元数据

到这里,是不是就知道Nest为什么能够在启动的时候就已经把ControllerService等相关依赖收集好并且完成创建对象了!!!

即把上面的{name: 'jmin'}对象替换为对应的依赖对象即可,如下图,Nest就是这样处理的:

我们通过调试方式进入源码来看看Module的实现

可以看到左边就已经获取到了metadata元数据,最终会调用Reflect.defineMetadataAppModule添加元数据,完成依赖收集,最终交给ICO容器统一接管。

看到这里就知道Nest的核心在于metadata,而实际上是通过reflect-metadata这个包进行polyfill的。

其实还有一个问题,实例化构造函数的时候,需要传递具体的参数,但又没有通过装饰器添加,是怎么做到的?比如AppController里面依赖了AppService,在getHello方法中可以直接拿到appService对象的getHello方法。

这就要提到一个typescript的编译选项emitDecoratorMetadata

Nest中默认开启了这个选项,代表着在编译阶段自动给装饰器添加元数据,我们拿一个编译之后的产物来看看。

还是上面的例子,可以看到findOne方法上面额外添加了几个元数据,分别为:

  • design:type: 代表着装饰对象的类型,这里是函数
  • design:paramtypes: 方法参数类型
  • design:returntype: 返回值类型,这个方法没有返回值,为undefined

翻了一下Nest源码,可以看到有定义对应的metadataKey

而在Nest启动的时候,会通过Reflect.getMetadata方法来拿到这个参数进行初始化。

所以这就是为了Nest使用TS的原因,必须依赖它的这个编译选项来实现依赖注入。

总结

好,这一节介绍了关于Reflect对象和metadata元数据在Nest中的承担着不可忽视的角色,而要实现装饰器效果用到了reflect-metadata这个库,它的用途很广,想要在Vue2中使用Composition API也是通过这个库来实现的,原理一样。

最后从这个metadata角度简单了解了Nest的实现原理,在Nest中,它的用途不仅于此,还有权限管理相关的设计也很巧妙的用到了它,后续讲到这块的时候再详细介绍。

相关推荐
仍然探索未知中15 分钟前
前端扫盲HTML
前端·html
蚂蚁在飞-35 分钟前
Golang基础知识—cond
开发语言·后端·golang
Brilliant Nemo1 小时前
Vue2项目中使用videojs播放mp4视频
开发语言·前端·javascript
酷爱码1 小时前
Linux实现临时RAM登录的方法汇总
linux·前端·javascript
LuckyLay1 小时前
Vue百日学习计划Day16-18天详细计划-Gemini版
前端·vue.js·学习
想要飞翔的pig2 小时前
uniapp+vue3页面滚动加载数据
前端·vue.js·uni-app
HarryHY2 小时前
git提交库常用词
前端
SoraLuna2 小时前
「Mac畅玩AIGC与多模态41」开发篇36 - 用 ArkTS 构建聚合搜索前端页面
前端·macos·aigc
霸王蟹2 小时前
React Fiber 架构深度解析:时间切片与性能优化的核心引擎
前端·笔记·react.js·性能优化·架构·前端框架
benben0442 小时前
Unity3D仿星露谷物语开发44之收集农作物
前端·游戏·unity·游戏引擎