装饰器
装饰模式(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;
};
}