Background: Decorators have recently been used in Vue2.6, and the amount of code and complexity has been greatly reduced. Let’s take a look at what decorators are, quoting a sentence summarized by someone else.

What is a decorator

Decorator is a new syntax in ES7, currently in phase 2 proposal. As it is called “Decorator,” it can wrap objects by adding the @ method name and then return a wrapped object. Decorators include classes, properties, methods, etc.

Method log

Start with a simple requirement, such as knowing whether a method is executing, executing successfully, and executing an exception.

Before modification

methods: {
    handleSubmit() {
        const fnName = 'handleSubmit'
        console.log(The 'method is executing${fnName}`)
        try {
            const param = {}
            param = 'xxx'
            console.log('method executed successfully${fnName}`)}catch (error) {
            console.log('method failed to execute${fnName}.${error}`)}}}Copy the code

After transforming

// utils/decorator.js
/** * Output method day log *@param {*} type* /
export const log = type= > {
  return (target, name, descriptor) = > {
    const method = descriptor.value
    descriptor.value = (. args) = > {
      console.info(` (${type}) is executing:${name}(${args}) = ?`)
      let ret
      try {
        ret = method.apply(target, args)
        console.info(` (${type}Success:${name}(${args}) = >${ret}`)}catch (error) {
        console.error(` (${type}Failure) :${name}(${args}) = >${error}`)}return ret
    }
  }
}
Copy the code
import { log } from '@utils/decorator.js'

methods: {
    @log()
    handleSubmit() {
        const param = {}
        param = 'xxx'}}Copy the code

Running effect

Now it’s time to print something that makes sense to the code, so keep reading.

Validation of Element UI Form before submission

When submitting to the background using Element UI’s form component, the parameters are usually validated first. The form’s API can be viewed here.

Before modification

// Suppose this.formEl is the form's $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('Commit failed')
        this.$message.success('Submitted successfully')}}Copy the code

After transforming

// utils/decorator.js
/** * form validation *@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
      constisValidate = _this[formElKey]? .validateif (isValidate) {
        const [, res] = await to(isValidate())
        if(! res)return false
      }
      return method.apply(_this, arguments)}}}Copy the code
import { formValidation } from '@utils/decorator.js'

methods: {
    @formValidation('formEl')
    handleSubmit() {
        const [err] = await to(this.$api.xx())
        if (err) return this.$message.error('Commit failed')
        this.$message.success('Submitted successfully')}}Copy the code

Are you starting to feel a sense of thinking, as if a lot of things can be decorated, continue to read, continue to release big moves.

Element UI’s asynchronous messageBox

We found that the official asynchronous messageBox writing method has a lot of code, while most of our asynchronous methods are different, the rest are basically all the same, even if you want to change the title or content, you can also change the parameter, please move here to check.

Before modification

methods: {
    handleSave() {
        this.$confirm('Are you sure you want to batch delete users? '.'Batch Delete users', {
            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 deleted successfully! ')
                done()
            }
          })
    }
}
Copy the code

After transforming

// utils/decorator.js
/** * confirmation box *@param {String} title- title *@param {String} concent- content *@param {String} confirmButtonText- Confirm the button name *@returns * /
export const confirm = (title, concent, confirmButtonText = 'sure') = > {
  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] = awaitto(method.call(_this, ... args), instance,'confirmButtonLoading')
          if (err) return console.error(err)
          done()
        }
      })
    }
  }
}
Copy the code
import { formValidation } from '@utils/decorator.js'

methods: {
    @confirm('Batch Delete users'.'Are you sure you want to batch delete users? '.'delete')
    async handleDel(ids) {
        const [err] = await this.$to(this.$api.delUser({ ids }))
        if (err) return
        this.$message.success('Batch deleted successfully! ')
        this.getData()
    }
}
Copy the code

Running effect

Image stabilization

// utils/decorator.js
/** * anti-shake, continuous operation, only the last trigger *@export
 * @param {Function} fun- Run the 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 the code
import { debounce } from '@utils/decorator.js'

methods: {
    @debounce(500)
    handleSubmit() {
        console.log("Just try it.")}}Copy the code

The throttle

// utils/decorator.js
/** * throttling, can only trigger one operation * in a certain period of time@export
 * @param {Function} fn- Run the 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 the code
import { throttle } from '@utils/decorator.js'

methods: {
    @throttle(500)
    handleSubmit() {
        console.log("Just try it.")}}Copy the code

Caching results

/** * Cache the result *@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 the code
import { cached } from '@utils/decorator.js'

methods: {
    @cached()
    handleSubmit(a, b, c) {
        console.log("Just try it.")
        return a + b + c
    }
}
Copy the code

Open/Close loading

/** * Automatically starts 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 the 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 the code

Feel free to leave a comment in the comments section if there are any errors or more ways to use this article!