什么是装饰器
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.value
descriptor.value = (...args) => {
console.info(`(${type}) 正在执行: ${name}(${args}) = ?`)
let ret
try {
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 是 表单的$ref
methods: {
async handleSubmit() {
const [validateErr] = await this.formEl.validate()
if (validateErr) return
const [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.value
descriptor.value = async function() {
const _this = this._isVue ? this : target
const isValidate = _this[formElKey]?.validate
if (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.value
descriptor.value = function (...args) {
const isUseFunction = (key) => toType(key, 'Function') ? key(...args) : key
const _this = this._isVue ? this : target
const _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) return
this.$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.value
let timer = null
descriptor.value = function() {
const _this = this._isVue ? this : target
clearTimeout(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.value
let canRun = true
descriptor.value = function() {
const _this = this._isVue ? this : target
if (!canRun) return
fn.apply(_this, arguments)
canRun = false
setTimeout(() => {
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.value
const cache = new Map()
descriptor.value = function() {
const _this = this._isVue ? this : target
const 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.value
descriptor.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)
}
}
}