装饰器
装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、访问符、属性或参数上。
装饰器目前还是一项实验性特性,想要使用装饰器,需要在tsconfig.json中开启选项:
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true // 开启装饰器
}
}
装饰器的写法为:@expression,以@开头,expression为一个函数。
function logController(target: any) {
console.log('log')
}
@logController
class Person {
name: string = ''
age: number = 0
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
const p = new Person('张三', 18) // log
上面是一个简单的装饰器示例代码,logController会被作用于类Person上。
除此之外,装饰器也可以传递参数:如@logController('hello'),至于如何传递参数以及接收参数,还有上面的target是什么,这些问题咱们在下面的内容介绍。
装饰器分类
装饰器分类装饰器、方法装饰器、参数装饰器、属性装饰器、访问器装饰器。不同装饰器所接收的参数也有所不同。
类装饰器
类装饰器顾名思义作用于类上,可以用来监视、修改或替换类定义。
类装饰器的参数有且只有一个:
target:类的构造函数。
const addSex: ClassDecorator = (target: any) => {
target.prototype.sex = '男'
}
@addSex
class Person {
name: string = ''
age: number = 0
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
interface P {
name: string
age: number
sex?: string
}
const p: P = new Person('张三', 18)
console.log(p.sex) // 男
上面我们通过装饰器为Person类增加了sex属性。
前面说过,装饰器也可以传递参数,要实现参数传递,只需要在装饰器内部返回一个装饰器函数即可,类似于匿名函数的形式。
const addSex = (sex: string): ClassDecorator => {
return (target: any) => {
target.prototype.sex = sex
}
}
@addSex('女')
class Person {
name: string = ''
age: number = 0
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
interface P {
name: string
age: number
sex?: string
}
const p: P = new Person('张三', 18)
console.log(p.sex) // 女
这种叫做装饰器工厂。
方法装饰器
方法装饰器对方法进行修饰,它接收3个参数:
target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。propertyKey: 成员的名字。descriptor: 成员的属性描述符。
const logResult = (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
const fn = descriptor.value
descriptor.value = function(...rest) {
const result = fn.apply(this, rest)
console.log(propertyKey + ':' + result)
return result
}
}
class Person {
name: string = ''
age: number = 0
constructor(name: string, age: number) {
this.name = name
this.age = age
}
@logResult
getName() {
return this.name
}
@logResult
getAge() {
return this.age
}
}
const p = new Person('张三', 18)
p.getName() // getName:张三
p.getAge() // getAge:18
方法装饰器的参数中比较重要的是第三个参数: 属性描述符descriptor。
descriptor包含configurable、enumerable、writable和value4个属性。这里的value指向我们装饰的函数,我们可以对其进行重写,实现打印内容:方法名+方法的返回结果。
参数装饰器
参数装饰器用于装饰函数参数,参数装饰器接收3个参数:
target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象propertyKey: 方法名。paramIndex: 参数所在位置的索引。
const paramDecorator = (target: any, propertyKey: string, paramIndex: number) => {
console.log(target, propertyKey, paramIndex)
}
class Person {
name: string = ''
age: number = 0
constructor(name: string, age: number) {
this.name = name
this.age = age
}
setName(@paramDecorator name: string) {
this.name = name
}
}
const p = new Person('张三', 18) // Person、setName、0
p.setName('李四')
属性装饰器
属性装饰器作用于类属性上,接收两个参数:
target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。propertyKey: 成员的名字。
const propertyName = (target: any, propertyKey: string) => {
console.log(target, propertyKey)
}
class Person {
@propertyName // Person, name
name: string = ''
age: number = 0
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
const p = new Person('张三', 18)
属性装饰器应用较少,很多文章用它来初始化属性值,实际上TS本就建议类属性在声明时进行初始化,所以用其来初始化属性值意义不大,引用用官方文档说的:属性装饰器用来监视类中是否声明了某个名字的属性。
访问器装饰器
访问器装饰器作业用于访问器上,接收3个参数:
target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。propertyKey: 方法名。descriptor: 属性描述符。
const getSetDecorator = (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
console.log(target, propertyKey, descriptor)
}
class Person {
private _name: string = ''
private _age: number = 0
constructor(name: string, age: number) {
this._name = name
this._age = age
}
@getSetDecorator
get name() {
return this._name
}
set name(name: string) {
this._name = name
}
}
const p = new Person('张三', 18)
TypeScript不允许同时装饰一个成员的get和set访问器,所以上面的set访问器不能够再添加装饰器,否则会报错。
实战例子
请求loading
在平时的业务开发中,我们往往会在请求接口数据时添加loading:
import { Loading } from 'element-ui'
export default {
methods: {
async fetchData() {
const loading = Loading.service()
try {
await api.getList()
} finally {
loading.close() // 关闭loading
}
}
}
}
通过装饰器实现后的样子:
export default {
methods: {
@loading()
async fetchData() {
await api.getList()
}
}
}
装饰器实现代码:
import { Loading } from 'element-ui'
/**
* 装饰器:loading
*/
export const loading = (text = '加载中') => {
return (target, name, descriptor) => {
const fn = descriptor.value
descriptor.value = async (...rest) => {
const loading = Loading.service({
text
})
try {
return await fn.apply(this, rest)
} finally {
loading.close()
}
}
}
}
确认弹窗
在进行删除操作时我们一般会弹出确认弹窗,确认弹窗的代码一般也都很固定,因此我们也可以写一个装饰器来简化这个过程。
import { MessageBox } from 'element-ui'
/**
* 装饰器:确认弹窗
*/
export const confirm = (message) => {
return (target, name, descriptor) => {
const fn = descriptor.value
descriptor.value = async (...rest) => {
await MessageBox.confirm(message)
fn.apply(this, rest)
}
}
}
// 使用装饰器
export default {
methods: {
@confirm('确认删除吗?')
async onDelete() {
await api.deleteUser()
}
}
}
以上两个例子只是装饰器应用于实际开发中的冰山一角,对装饰器感兴趣的朋友可以多深入了解并实践,实现更复杂的应用场景。
最后
本次分享到这里就结束了,感谢您的阅读。