- 使用装饰器前,需要把 tsconfig.json 中
experimentalDecorators
设置为 true- 学习了小满的B站课程:https://www.bilibili.com/video/BV1wR4y1377K?p=24
前言
ts中有几种装饰器类型:
- 类装饰器
ClassDecorator
- 方法装饰器
MethodDecorator
- 参数装饰器
ParameterDecorator
- 属性装饰器
PropertyDecorator
一、类装饰器 ClassDecorator
- 功能 & 使用场景:
不想破坏原有的类的结构
,又不想去读内部的代码逻辑,只想给类增加一些新的属性或方法
,可对该类使用装饰器 - 装饰器需要定义为
ClassDecorator
类型 - 装饰器回传参数
target
, 是调用者的构造函数
- 两种使用方式:
- 在 class 前
@DecoratorName
- 在 class 后
DecoratorName(className)
- 在 class 前
1、类装饰器的简单使用
const DecoratorClassDemo: ClassDecorator = (target) => {
// 回传的 target 是调用者的构造函数
// 可以额外定义属性、方法
target.prototype.color = 'blue'
target.prototype.showColor = () => {
console.log('哈哈哈哈')
}
}
// 写法一:若担心浏览器不支持,可以使用写法二
@DecoratorClassDemo
class Http {
// 不想读里面的逻辑,又想加一些东西,并且不破坏原有的结构,可以考虑在外层使用装饰器
}
// 写法二:
// DecoratorClassDemo(Http)
// 将类实例化后,可以访问到装饰器里新增的属性和方法
const http = new Http() as any
console.log('http.color', http.color) // blue
http.showColor()
如果想要 传参数
给类装饰器,怎么做?
2、装饰器工厂
可以先把它定义为 普通函数
(保证参数的接收),然后在其 内部返回一个装饰器函数
即可(闭包)
// 可以先把它定义为普通函数(保证参数的接收),然后在其内部返回一个装饰器函数即可
const DecoratorClassDemo2 = (msg: string, age: number) => {
const fn: ClassDecorator = (target) => {
target.prototype.msg = msg
target.prototype.age = age
}
return fn
}
@DecoratorClassDemo2('hahaha', 18)
class Http2 {
// .....
}
const http2 = new Http2() as any
console.log('类装饰器传参---', http2.msg)
console.log('类装饰器传参---', http2.age)
同样的方式在其他装饰器中也有使用
二、方法装饰器 MethodDecorator
- 顾名思义,就是对
方法
使用装饰器,在方法前@DecoratorName
- 方法装饰器需要声明为
MethodDecorator
类型 - 方法装饰器回传参数:
target, propertyKey, descriptor
target
:调用者的原型
propertyKey
: 调用者的 key,即方法名称
descriptor
:调用者的描述
方法装饰器
想要回调数据给调用者
,需要 把descriptor
声明为PropertyDescriptor
类型,通过descriptor.value
返回
例:写一个简单的Get请求装饰器
// 安装:npm i axios
import axios from 'axios'
const Get = (url: string) => {
// 这里注意把 descriptor 定义为 PropertyDescriptor 类型
const fn: MethodDecorator = (target, propertyKey, descriptor: PropertyDescriptor) => {
// 【target】:{constructor: ƒ, getList: ƒ}
// 【propertyKey】:getList
// 【descriptor】:{writable: true, enumerable: false, configurable: true, value: ƒ}
axios.get(url).then(res => {
// 拿到结果后通过 descriptor 的 value 返回(注意value是个函数)
descriptor.value(res.data)
})
}
return fn
}
class Http3 {
// 对 getList 使用 方法装饰器 Get
@Get('https://api.apiopen.top/api/getHaoKanVideo?page=1&size=10')
getList (data: any) {
console.log(data)
}
}
最终打印出的 data:
三、参数装饰器 ParameterDecorator
- 顾名思义,对
参数
使用装饰器,在参数前@DecoratorName
- 参数装饰器需要定义为
ParameterDecorator
类型 - 参数装饰器回传参数:
target, propertyKey, parameterIndex
target
:调用者的原型
propertyKey
:调用者的方法名称
parameterIndex
:该参数是方法中的第几个参数
- 需要用到插件
reflect-metadata
,使用元数据
- 安装命令:
npm i reflect-metadata
- tsconfig.json 中需要将
emitDecoratorMetadata
开启 Reflect.defineMetadata
:存入元数据
,参数如下:metadataKey
: 所要存元数据的key
metadataValue
: 对应的value
target
: 存入的目标
Reflect.getMetadata
:取出元数据
,参数如下:metadataKey
:所要取的元数据的key
target
:所要取的目标
- 安装命令:
在上面的基础上继续造例子,将 参数data 处理为优先返回内部的 result
import axios from 'axios'
import 'reflect-metadata'
const Result = () => {
const fn: ParameterDecorator = (target, propertyKey, parameterIndex) => {
// 【target】:{constructor: ƒ, getList: ƒ}
// 【propertyKey】:getList
// 【parameterIndex】:0
// 存入数据:key='result'
Reflect.defineMetadata('key', 'result', target)
}
return fn
}
const Get2 = (url: string) => {
const fn: MethodDecorator = (target, propertyKey, descriptor: PropertyDescriptor) => {
axios.get(url).then(res => {
// 在这里取出所存元数据的 key 所存的 value(字符串 result) ,看返回的数据中有没有这个值,有的话直接返回
const k = Reflect.getMetadata('key', target)
descriptor.value(k ? res.data[k] : res.data)
})
}
return fn
}
class Http4 {
@Get2('https://api.apiopen.top/api/getHaoKanVideo?page=1&size=10')
// 比如:对这里的 参数data 使用装饰器 Result,目的是自动取出 data 中的 result 属性
// @Result 优先于 @Get2 先执行,在 @Result 存元数据,在 @Get2 中取元数据
getList(@Result() data: any) {
console.log(data)
}
}
经过参数装饰器后,打印出的 data:
四、属性装饰器 PropertyDecorator
属性
的装饰器,在属性前@DecoratorName
- 属性装饰器需要定义为
PropertyDecorator
类型 - 属性装饰器回传参数:
target, propertyKey
target
:调用者的原型
propertyKey
: 调用者的属性名字
const Color: PropertyDecorator = (target, propertyKey) => {
// 【target】:{constructor: ƒ}
// 【propertyKey】:color
// 一些操作...
}
class Http5 {
// 对 属性color 使用 属性装饰器Color
@Color
color:string
constructor () {
this.color = 'blue'
}
}
看的时候很懵逼,一定要多敲才能理解深刻!!
如果只看文章太痛苦,可以看文章最上面引言中提到的B站视频教程,然后再敲一遍