Skip to main content

装饰器

装饰模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。 这种模式属于结构型模式,它是作为现有的类的一个包装。 这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。 优点:

  • 不需要通过创建子类的方式去拓展功能(不需要子类化),这样可以避免代码臃肿的问题
  • 装饰类的方法复用性很高
  • 不会影响到原对象的代码结构
  • 语义化

增加类的自带状态

// 含参数的装饰器
function testable(isTestable) {
return function (target) {
target.isTestable = isTestable;
};
}

@testable(true)
class MyTestableClass {}

MyTestableClass.isTestable; // true

日志

// 方法装饰器,如日志装饰器
function log(target, name, descriptor) {
// descriptor是一个对象,其value属性是被装饰的原对象
var oldValue = descriptor.value;

// 可以把原对象暗渡替换
descriptor.value = function () {
console.log(`Calling ${name} with`, arguments);
return oldValue.apply(this, arguments);
};

return descriptor;
}

class Math {
@log
add(a, b) {
return a + b;
}
}

const math = new Math();

// Calling add with arguments
math.add(2, 4);

try catch 装饰

export function CatchError() {
// 函数返回了一个装饰器对象,因此真正使用时要@CatchError()调用
// 其实没啥区别,可能是作为方法装饰能保持一致
return function (target: any, name: string, descriptor: PropertyDescriptor) {
const fn = descriptor.value;
descriptor.value = async function (...args: any[]) {
// 重写函数并包上try catch
try {
await fn.call(this, ...args);
} catch (error) {
console.log("error", error);
}
};
// 返回新的descriptor
return descriptor;
};
}

import { CatchError } from "@/utils/decorator";
export default class TestForm extends Vue {
@CatchError()
async submitForm() {
await this.handleTest();
this.$message.success("Submit Successfully!");
}
}

Vue 弹出框

<template>
<div>
<el-button type="text" @click="handleDelete"
>点击打开 Message Box 提示是否删除</el-button
>
</div>
</template>

<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
import { Confirmation } from "@/utils/decorator";
@Component
export default class DecoratorTest extends Vue {
// 装饰器中能传参
@Confirmation({
title: "提示",
message: "此操作将永久删除该文件, 是否继续?",
})
// 不改变业务逻辑
handleDelete(instance: any, done: any) {
setTimeout(() => {
done();
setTimeout(() => {
instance.confirmButtonLoading = false;
this.$message({
type: "success",
message: "删除成功!",
});
}, 300);
}, 2000);
}
}
</script>
import Vue from "vue";
interface ConfirmationConfig {
title: string;
message: string;
// eslint-disable-next-line @typescript-eslint/ban-types
options?: object;
type?: string;
}
export function Confirmation(config: ConfirmationConfig) {
return function (target: any, name: string, descriptor: PropertyDescriptor) {
const fn = descriptor.value;
let _instance: any = null;
descriptor.value = function (...args: any[]) {
// 调用Vue上confirm功能
Vue.prototype
.$confirm(
config.message,
config.title,
Object.assign(
{
beforeClose: (action: string, instance: any, done: any) => {
_instance = instance;
if (action === "confirm") {
instance.confirmButtonLoading = true;
fn.call(this, instance, done, ...args);
} else {
done();
}
},
},
config.options || {}
)
)
.then(() => {
_instance.confirmButtonLoading = false;
});
};
return descriptor;
};
}

参考

https://juejin.cn/post/7106441797567512583