What is a decorator
Decorator is a new syntax of ES7. It is still in the second stage proposal. As expressed by the name of its "decorator", it can decorate and wrap some objects by adding @ method name, and then return a wrapped object. The objects that can be decorated include classes, properties, methods, etc.
Method log
Let's start with a simple requirement, such as whether a method is being executed, successfully executed and abnormally executed. Normally, it will be as follows.
Before transformation
methods: { handleSubmit() { const fnName = 'handleSubmit' console.log(`Method is executing ${fnName}`) try { const param = {} param = 'xxx' console.log(`Method executed successfully ${fnName}`) } catch (error) { console.log(`Method execution failed ${fnName},${error}`) } } } Copy code
After transformation
// utils/decorator.js /** * Output method daily log * @param {*} type */ export const log = type => { return (target, name, descriptor) => { const method = descriptor.value descriptor.value = (...args) => { console.info(`(${type}) Executing: ${name}(${args}) = ?`) let ret try { ret = method.apply(target, args) console.info(`(${type}) success : ${name}(${args}) => ${ret}`) } catch (error) { console.error(`(${type}) fail: ${name}(${args}) => ${error}`) } return ret } } } Copy code
import { log } from '@utils/decorator.js' methods: { @log() handleSubmit() { const param = {} param = 'xxx' } } Copy code
Operation effect
Next, start outputting something meaningful to the code. Continue to look down.
Element ui Form verification before submission
When submitting to the background using the form component of Element ui, the parameters are usually verified first. Please step here to view the specific API of the form.
Before transformation
// Suppose this.formEl is the $ref of the form 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('Submission failed') this.$message.success('Submitted successfully') } } Copy code
After transformation
// utils/decorator.js /** * Form verification * @param {String} formElKey - Form 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) } } } Copy code
import { formValidation } from '@utils/decorator.js' methods: { @formValidation('formEl') handleSubmit() { const [err] = await to(this.$api.xx()) if (err) return this.$message.error('Submission failed') this.$message.success('Submitted successfully') } } Copy code
Are you beginning to feel that you have ideas? It seems that many things can use decorators. Continue to look down and release big moves.
Asynchronous messageBox for Element ui
It is found that the official asynchronous messageBox writing method has a large amount of code, but most of our asynchronous methods are different. The rest are basically the same. Even if you want to change a title or content, you can change a parameter. Please step here to check.
Before transformation
methods: { handleSave() { this.$confirm('Are you sure to delete users in batch?', 'Batch delete user', { dangerouslyUseHTMLString: true, distinguishCancelAndClose: true, confirmButtonText: 'delete', beforeClose: async (action, instance, done) => { if (action !== 'confirm') return done() instance.confirmButtonText = 'In execution...' const [err] = await this.$to(this.$api.delUser({ ids })) if (err) return done() this.$message.success('Batch deletion succeeded!') done() } }) } } Copy code
After transformation
// utils/decorator.js /** * Confirmation box * @param {String} title - title * @param {String} concent - content * @param {String} confirmButtonText - Confirm button name * @returns */ export const confirm = (title, concent, confirmButtonText = 'determine') => { 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 = 'In execution...' const [err] = await to(method.call(_this, ...args), instance, 'confirmButtonLoading') if (err) return console.error(err) done() } }) } } } Copy code
import { formValidation } from '@utils/decorator.js' methods: { @confirm('Batch delete user', 'Are you sure to delete users in batch?', 'delete') async handleDel(ids) { const [err] = await this.$to(this.$api.delUser({ ids })) if (err) return this.$message.success('Batch deletion succeeded!') this.getData() } } Copy code
Operation effect
Anti shake
// utils/decorator.js /** * Anti shake, continuous operation, only triggered at the last time * @export * @param {Function} fun - Run function * @param {Number} wait - delay time * @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) } } } Copy code
import { debounce } from '@utils/decorator.js' methods: { @debounce(500) handleSubmit() { console.log('Just try') } } Copy code
throttle
// utils/decorator.js /** * Throttling can only trigger one operation within a certain time * @export * @param {Function} fn - Run function * @param {Number} wait - delay time * @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) } } } Copy code
import { throttle } from '@utils/decorator.js' methods: { @throttle(500) handleSubmit() { console.log('Just try') } } Copy code
Cache calculation results
/** * Cache calculation results * @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) } } } Copy code
import { cached } from '@utils/decorator.js' methods: { @cached() handleSubmit(a, b, c) { console.log('Just try') return a + b + c } } Copy code
Turn on / off loading
/** * Automatically start 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 // open const [err, result] = await to(method.apply(_this, arguments)) _this[loadingKey] = false // close return err || result } } } Copy code
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) } } } Copy code
last
If you think this article is a little helpful to you, give it a compliment. Or you can join my development exchange group: 1025263163 learn from each other, and we will have professional technical Q & A to solve doubts
If you think this article is useful to you, please click star: http://github.crmeb.net/u/defu esteem it a favor!