TypeScript Reflect Metadata
本节介绍的 Reflect Metadata 主要用来在声明的时候添加和读取元数据。通过这种方式给对象添加额外的信息,是不会影响对象的结构的。
1. 慕课解释
Reflect,翻译为『反射』,Metadata,翻译为『元数据』。反射这个概念在 Java 等众多语言中已经广泛运用,Reflect Metadata 就是通过装饰器来给类添加一些自定义的信息,然后通过反射将这些信息提取出来,也可以通过反射来添加这些信息。
2. 安装使用
通过 npm 安装这个库:
npm i reflect-metadata --save
而且需要在 tsconfig.json
中配置:
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
命令行使用:
tsc --target ES5 --experimentalDecorators --emitDecoratorMetadata
当启用后,只要 reflect-metadata
库被引入了,设计阶段添加的类型信息可以在运行时使用。
import 'reflect-metadata'
@Reflect.metadata('token', 'aW1vb2M=')
class Employee {
@Reflect.metadata('level', 'D2')
salary() {
console.log('这是个秘密')
}
@Reflect.metadata('times', 'daily')
static meeting() {}
}
const token = Reflect.getMetadata('token', Employee)
const level = Reflect.getMetadata('level', new Employee(), 'salary')
const times = Reflect.getMetadata('times', Employee, 'meeting')
console.log(token) // aW1vb2M=
console.log(level) // D2
console.log(times) // daily
TIPS: 注意, 实例方法与静态方法取元数据是不同的,实例方法需要在类的实例上取元数据,静态方法直接在类上取元数据。
3. API
import 'reflect-metadata'
// 元数据的命令式定义,定义对象或属性的元数据
Reflect.defineMetadata(metadataKey, metadataValue, target)
Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey)
// 检查对象或属性的原型链上是否存在元数据键
let result = Reflect.hasMetadata(metadataKey, target)
let result = Reflect.hasMetadata(metadataKey, target, propertyKey)
// 检查对象或属性是否存在自己的元数据键
let result = Reflect.hasMetadata(metadataKey, target)
let result = Reflect.hasMetadata(metadataKey, target, propertyKey)
// 获取对象或属性原型链上元数据键的元数据值
let result = Reflect.getMetadata(metadataKey, target)
let result = Reflect.getMetadata(metadataKey, target, propertyKey)
// 获取对象或属性的自己的元数据键的元数据值
let result = Reflect.getOwnMetadata(metadataKey, target)
let result = Reflect.getOwnMetadata(metadataKey, target, propertyKey)
// 获取对象或属性原型链上的所有元数据键
let result = Reflect.getMetadataKeys(target)
let result = Reflect.getMetadataKeys(target, propertyKey)
// 获取对象或属性的所有自己的元数据键
let result = Reflect.getOwnMetadataKeys(target)
let result = Reflect.getOwnMetadataKeys(target, propertyKey)
// 从对象或属性中删除元数据
let result = Reflect.deleteMetadata(metadataKey, target)
let result = Reflect.deleteMetadata(metadataKey, target, propertyKey)
// 通过装饰器将元数据应用于构造函数
@Reflect.metadata(metadataKey, metadataValue)
class C {
// 通过装饰器将元数据应用于方法(属性)
@Reflect.metadata(metadataKey, metadataValue)
method() {
}
}
4. 结合装饰器使用
Reflect Metadata 结合上节介绍的装饰器:
import 'reflect-metadata'
function get(path: string): MethodDecorator {
return (target, name) => {
Reflect.defineMetadata('path', path, target, name)
}
}
class Employee {
@get('/init')
async init() {}
}
const metadata = Reflect.getMetadata('path', new Employee(), 'init')
console.log(metadata) // '/init'
解释: 如果经常开发 Node.js 的同学对这样的写法是不是有些熟悉呢?类方法 init()
上的装饰器 get()
传入元数据 '/init'
,再通过反射拿到这个路由信息,将这些路由信息进行一定的封装,然后绑定在 koa-router
上,就能达到自动加载路由的功能。
5. 小结
本节介绍了 Reflect Metadata 的一些基础使用方式,一些基础库源码如 vue-class-component
、Angular
均使用了 Reflect Metadata ,有兴趣的可以深入源码学习下。