装饰器与元数据
启用实验性装饰器,编写类、方法和属性装饰器,并使用 reflect-metadata 实现依赖注入模式。
装饰器与元数据 是 CoddyKit 上的免费 Frontend Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Frontend Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Frontend Academy 课程共包含 4 节课。
什么是装饰器
装饰器是在定义时修改类、方法、属性或参数的函数。Angular、NestJS、TypeORM 和基于类的 MobX 大量使用装饰器。
装饰器语法
在类、方法、属性或参数前加上 @DecoratorName。装饰器函数会在类定义时运行。
@Component({ selector: 'app-root' })
export class AppComponent {
@Input() title: string;
@HostListener('click')
onClick() {
console.log('clicked');
}
}启用装饰器
在 tsconfig.json 中启用实验性标志。新的 ECMAScript 第 3 阶段装饰器(TypeScript 5.0+)不需要此标志,但语义有所不同。
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}类装饰器
它会接收构造函数作为参数,并可以替换、包装或扩展类。
function Singleton<T extends { new (...a: any[]): {} }>(target: T) {
let instance: T;
return class extends target {
constructor(...args: any[]) {
if (!instance) instance = new target(...args) as T;
return instance;
}
};
}
@Singleton
class Logger { /* ... */ }方法装饰器
它会接收(目标、键、描述符)。您可以用它包装方法,例如记录调用。
function Log(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`[${key}]`, args);
return original.apply(this, args);
};
}
class Service {
@Log
greet(name: string) { return `Hello ${name}`; }
}属性装饰器
它会接收(目标、键)。通常与 reflect-metadata 一起使用,用于注册属性相关信息。
function Required(target: any, key: string) {
const existing = Reflect.getMetadata('required', target) || [];
Reflect.defineMetadata('required', [...existing, key], target);
}
class User {
@Required name: string;
@Required email: string;
}参数装饰器
它会接收(目标、键、parameterIndex)。用于依赖注入,记录哪些参数需要哪些服务。
function Inject(token: string) {
return function (target: any, key: string, index: number) {
const existing = Reflect.getMetadata('inject', target, key) || {};
existing[index] = token;
Reflect.defineMetadata('inject', existing, target, key);
};
}
class UserService {
constructor(@Inject('Database') db: Database) {}
}reflect-metadata
reflect-metadata 是一个 polyfill,允许装饰器读取和写入类的元数据。Angular DI 和许多 ORM 都需要它。
npm install reflect-metadata
// main.ts (entry point):
import 'reflect-metadata';
import { Reflect } from 'reflect-metadata';
Reflect.defineMetadata('key', 'value', target);
Reflect.getMetadata('key', target); // 'value'装饰器工厂
工厂会返回一个装饰器,用于传入参数。
function Component(config: { selector: string }) {
return function (target: any) {
target.selector = config.selector;
};
}
@Component({ selector: 'app-root' })
class AppComponent {}验证应用场景
将属性装饰器与验证函数结合起来,即可构建一个小型验证库。
// Decorator records that a field is required:
function Required(target: any, key: string) {
const fields = Reflect.getMetadata('required', target) || [];
Reflect.defineMetadata('required', [...fields, key], target);
}
class User {
@Required name!: string;
@Required email!: string;
}
function validate(obj: any): string[] {
const required: string[] = Reflect.getMetadata('required', obj) || [];
return required.filter(k => obj[k] == null);
}第 3 阶段装饰器(TC39)
标准化的装饰器(TS 5.0+,Node 22+ 在启用标志后支持)具有不同的签名。它们不需要实验性标志,也不需要 reflect-metadata。
何时使用装饰器
在框架提供装饰器的地方使用它们(Angular、NestJS、TypeORM)。不要在普通前端代码中自行创建装饰器,因为它们会增加复杂性,并使代码依赖特定的运行时行为。
快速检查
要使用带有元数据反射的旧版(实验性)装饰器,必须启用哪些 tsconfig 标志?
回顾:装饰器
装饰器会在定义时修改类、方法、属性或参数。请启用 experimentalDecorators 和 emitDecoratorMetadata。类、方法、属性和参数装饰器的签名各不相同。reflect-metadata 用于存储元数据;工厂用于传入参数。Angular、NestJS 和 TypeORM 大量使用装饰器。第 3 阶段装饰器(TS 5+)是未来的发展方向,不需要任何标志。
常见问题解答
「装饰器与元数据」课时是免费的吗?
是的 — 「装饰器与元数据」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Frontend Academy 课程的其余内容,请升级到 CoddyKit PRO。 Frontend Academy 课程共包含 4 节课。
「装饰器与元数据」这节课中我会学到什么?
启用实验性装饰器,编写类、方法和属性装饰器,并使用 reflect-metadata 实现依赖注入模式。 你通过在浏览器中直接运行的动手代码来练习 Frontend Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Frontend Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Frontend Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「装饰器与元数据」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Frontend Academy 课中编写并运行代码吗?
能。每节 Frontend Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。