装饰器(Decorators)是 TypeScript 中一种特殊的语法,用于在类、方法、属性等元素上附加元数据或修改其行为。装饰器提供了一种在不改变类的定义的情况下,对类进行扩展或修改的方式。
装饰器的使用类似于注解,在代码中以 @
符号开头,后面紧跟装饰器工厂函数或表达式。装饰器工厂函数会在装饰器被应用到类、方法等上时被调用,它接受不同的参数,具体取决于被装饰的目标。
其实装饰器就是一个可以提前拿到类本身(或原型对象)并预处理的函数。当类定义的时候被调用。
使用装饰器之前,需要先在 tsconfig.json
中开启两个配置:
以下是一些常见的装饰器的用法和示例:
- 参数装饰器
import axios from 'axios'
// 使用 defineMetadata 需要 npm i reflect-metadata
import 'reflect-metadata'
const Get = (url: string) => {
// _ 是一个占位符,防止和下面的 key 重名
const fn: MethodDecorator = (target, _, descriptor: PropertyDescriptor) => {
// console.log(target, key, descriptor)
const key = Reflect.getMetadata('key',target)
axios.get(url).then(res => {
descriptor.value(key ? res.data[key] : res.data)
})
}
return fn
}
const Result = () => {
const fn: ParameterDecorator = (target, key, index) => {
Reflect.defineMetadata('key', 'result', target)
// console.log(target, key, index) // {} getList 0
}
return fn
}
class Http {
@Get('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
// @Result() 参数装饰器
getList(@Result() data: any) {
console.log(data);
}
}
-
类装饰器:
类装饰器用于装饰类的声明。它接受一个参数,即被装饰的类的构造函数。
function logClassName(constructor: Function) {
console.log(`Class name: ${constructor.name}`);
}
@logClassName
class MyClass {
// ...
}
// 写成匿名函数形式:
const Base:ClassDecorator = (target) =>{
console.log(target)
target.prototype.xx = 'xx'
target.prototype.fn = () =>{
console.log('fn')
}
}
@Base
class Http {
// ...
}
const http = new Http() as any
// Base(Http) // 删掉 @Base,写成这样也是可以的
http.fn()
-
方法装饰器:
方法装饰器用于装饰类的方法。它接受三个参数:目标类的原型,方法名称,方法的属性描述符。
function logMethod(target: any, methodName: string, descriptor: PropertyDescriptor) {
console.log(`Method ${methodName} is decorated.`);
}
class MyComponent {
@logMethod
doSomething() {
// ...
}
}
// 实例:
import axios from 'axios'
const Get = (url: string) => {
const fn: MethodDecorator = (target, key, descriptor: PropertyDescriptor) => {
console.log(target, key, descriptor)
axios.get(url).then(res => {
descriptor.value(res.data)
})
}
return fn
}
class Http {
@Get('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
getList(data: any) {
console.log(data.result.list)
}
}
-
属性装饰器:
属性装饰器用于装饰类的属性。它接受两个参数:目标类的原型和属性名称。
function readonly(target: any, propertyName: string) {
Object.defineProperty(target, propertyName, { writable: false });
}
class Person {
@readonly
name: string = "Alice";
}
-
装饰器工厂:
装饰器工厂是一个函数,用于生成装饰器。它可以接受参数,用于传递额外的配置信息。
function log(message: string) {
return function(target: any, propertyName: string) {
console.log(`Logged message: ${message}`);
};
}
class Product {
@log("Product created")
name: string;
}
需要注意的是,装饰器的执行顺序是从上往下的,即从最外层的装饰器开始执行。装饰器可以叠加在一起,对同一个目标进行多次装饰。