什么是装饰器
Decorator 是 ES7 的一个新语法,目前仍处于第2阶段提案中,正如其“装饰器”的叫法所表达的,他通过添加@方法名可以对一些对象进行装饰包装然后返回一个被包装过的对象,可以装饰的对象包括:类,属性,方法等。
不能装饰函数 存在函数变量提升 无效果
方法日志 log
先从一个简单需求说起,比如要知道一个方法是否正在执行中、是否执行成功和是否执行异常,正常会如下这个。
改造前
methods: {handleSubmit() {const fnName = 'handleSubmit'console.log(`方法正在执行 ${fnName}`)try {const param = {}param = 'xxx'console.log(`方法执行成功 ${fnName}`)} catch (error) {console.log(`方法执行失败 ${fnName},${error}`)}}}
改造后
// utils/decorator.js/*** 输出方法日日志* @param {*} type*/export const log = type => {return (target, name, descriptor) => {const method = descriptor.valuedescriptor.value = (...args) => {console.info(`(${type}) 正在执行: ${name}(${args}) = ?`)let rettry {ret = method.apply(target, args)console.info(`(${type}) 成功 : ${name}(${args}) => ${ret}`)} catch (error) {console.error(`(${type}) 失败: ${name}(${args}) => ${error}`)}return ret}}}
import { log } from '@utils/decorator.js'methods: {@log()handleSubmit() {const param = {}param = 'xxx'}}
Element ui Form 提交前校验
在使用Element ui的表单组件进行提交到后台时,通常会先验证参数,表单的具体API请步移此处查看。
改造前
// 假设 this.formEl 是 表单的$refmethods: {async handleSubmit() {const [validateErr] = await this.formEl.validate()if (validateErr) returnconst [err] = await to(this.$api.xx())if (err) return this.$message.error('提交失败')this.$message.success('提交成功')}}
改造后
// utils/decorator.js/*** 表单校验* @param {String} formElKey - 表单el*/export const formValidation = (formElKey = 'formEl') => {return (target, name, descriptor) => {const method = descriptor.valuedescriptor.value = async function() {const _this = this._isVue ? this : targetconst isValidate = _this[formElKey]?.validateif (isValidate) {const [, res] = await to(isValidate())if (!res) return false}return method.apply(_this, arguments)}}}
import { formValidation } from '@utils/decorator.js'methods: {@formValidation('formEl')handleSubmit() {const [err] = await to(this.$api.xx())if (err) return this.$message.error('提交失败')this.$message.success('提交成功')}}
是不是开始觉得有思路了,好像很多东西都可以用装饰器了,继续往下看,持续释放大招。
Element ui 的 异步messageBox
发现官方的异步messageBox写法,代码量很多,而我们大多数是异步方法不同,其余都基本一直,即使想变个title或者content,也可以改个参数就好了,请步移此处查看。
改造前
methods: {handleSave() {this.$confirm('确定执行批量删除用户吗?', '批量删除用户', {dangerouslyUseHTMLString: true,distinguishCancelAndClose: true,confirmButtonText: '删除',beforeClose: async (action, instance, done) => {if (action !== 'confirm') return done()instance.confirmButtonText = '执行中...'const [err] = await this.$to(this.$api.delUser({ ids }))if (err) return done()this.$message.success('批量删除成功!')done()}})}}
改造后
// utils/decorator.js/*** 确认框* @param {String} title - 标题* @param {String} concent - 内容* @param {String} confirmButtonText - 确认按钮名称* @returns*/export const confirm = (title, concent, confirmButtonText = '确定') => {return (target, name, descriptor) => {const method = descriptor.valuedescriptor.value = function (...args) {const isUseFunction = (key) => toType(key, 'Function') ? key(...args) : keyconst _this = this._isVue ? this : targetconst _title = isUseFunction(title)const _concent = isUseFunction(concent)return _this.$confirm(_concent, _title, {dangerouslyUseHTMLString: true,distinguishCancelAndClose: true,confirmButtonText: confirmButtonText,beforeClose: async (action, instance, done) => {if (action !== 'confirm') return done()instance.confirmButtonText = '执行中...'const [err] = await to(method.call(_this, ...args), instance, 'confirmButtonLoading')if (err) return console.error(err)done()}})}}}
import { formValidation } from '@utils/decorator.js'methods: {@confirm('批量删除用户', '确定执行批量删除用户吗?', '删除')async handleDel(ids) {const [err] = await this.$to(this.$api.delUser({ ids }))if (err) returnthis.$message.success('批量删除成功!')this.getData()}}
防抖
// utils/decorator.js/*** 防抖,连续操作时,只在最后一次触发* @export* @param {Function} fun - 运行函数* @param {Number} wait - 延迟时间* @returns*/export function debounce(wait) {return function(target, name, descriptor) {const fn = descriptor.valuelet timer = nulldescriptor.value = function() {const _this = this._isVue ? this : targetclearTimeout(timer)timer = setTimeout(() => {fn.apply(_this, arguments)}, wait)}}}
import { debounce } from '@utils/decorator.js'methods: {@debounce(500)handleSubmit() {console.log('试试就试试')}}
节流
// utils/decorator.js/*** 节流,一定时间内,只能触发一次操作* @export* @param {Function} fn - 运行函数* @param {Number} wait - 延迟时间* @returns*/export function throttle(wait) {return function(target, name, descriptor) {const fn = descriptor.valuelet canRun = truedescriptor.value = function() {const _this = this._isVue ? this : targetif (!canRun) returnfn.apply(_this, arguments)canRun = falsesetTimeout(() => {canRun = true}, wait)}}}
import { throttle } from '@utils/decorator.js'methods: {@throttle(500)handleSubmit() {console.log('试试就试试')}}
缓存计算结果
/*** 缓存计算结果* @export* @param {Function} fn* @returns*/export function cached() {return function(target, name, descriptor) {const method = descriptor.valueconst cache = new Map()descriptor.value = function() {const _this = this._isVue ? this : targetconst key = JSON.stringify(arguments)if (!cache.has(key)) {cache.set(key, method.apply(_this, arguments))}return cache.get(key)}}}
import { cached } from '@utils/decorator.js'methods: {@cached()handleSubmit(a, b, c) {console.log('试试就试试')return a + b + c}}
开启关闭loading
/*** 自动开启loading* @export* @param {string} [loadingKey='loading']* @returns*/export function autoSwitch(loadingKey = 'loading') {return function(target, name, descriptor) {const method = descriptor.valuedescriptor.value = async function() {const _this = this._isVue ? this : target_this[loadingKey] = true // 开启const [err, result] = await to(method.apply(_this, arguments))_this[loadingKey] = false // 关闭return err || result}}}
import { autoSwitch } from '@utils/decorator.js'methods: {@autoSwitch('loading')async handleSubmit() {try {const res = this.$api.xx()console.log(res)} catch (error) {console.log(error)}}}

