原文地址

装饰器 Decorator

什么是装饰器

Decorator 是 ES7 的一个新语法,目前仍处于第2阶段提案中,正如其“装饰器”的叫法所表达的,他通过添加@方法名可以对一些对象进行装饰包装然后返回一个被包装过的对象,可以装饰的对象包括:类,属性,方法等。
不能装饰函数 存在函数变量提升 无效果

方法日志 log

先从一个简单需求说起,比如要知道一个方法是否正在执行中、是否执行成功和是否执行异常,正常会如下这个。

改造前

  1. methods: {
  2. handleSubmit() {
  3. const fnName = 'handleSubmit'
  4. console.log(`方法正在执行 ${fnName}`)
  5. try {
  6. const param = {}
  7. param = 'xxx'
  8. console.log(`方法执行成功 ${fnName}`)
  9. } catch (error) {
  10. console.log(`方法执行失败 ${fnName},${error}`)
  11. }
  12. }
  13. }

改造后

  1. // utils/decorator.js
  2. /**
  3. * 输出方法日日志
  4. * @param {*} type
  5. */
  6. export const log = type => {
  7. return (target, name, descriptor) => {
  8. const method = descriptor.value
  9. descriptor.value = (...args) => {
  10. console.info(`(${type}) 正在执行: ${name}(${args}) = ?`)
  11. let ret
  12. try {
  13. ret = method.apply(target, args)
  14. console.info(`(${type}) 成功 : ${name}(${args}) => ${ret}`)
  15. } catch (error) {
  16. console.error(`(${type}) 失败: ${name}(${args}) => ${error}`)
  17. }
  18. return ret
  19. }
  20. }
  21. }
  1. import { log } from '@utils/decorator.js'
  2. methods: {
  3. @log()
  4. handleSubmit() {
  5. const param = {}
  6. param = 'xxx'
  7. }
  8. }

运行效果
2022/01/24 *【✨vue2.6 装饰器,用了就会真香🤣】 - 图1
接下来开始输出一些对代码有意义的了,继续往下看。

Element ui Form 提交前校验

在使用Element ui的表单组件进行提交到后台时,通常会先验证参数,表单的具体API请步移此处查看。

改造前

  1. // 假设 this.formEl 是 表单的$ref
  2. methods: {
  3. async handleSubmit() {
  4. const [validateErr] = await this.formEl.validate()
  5. if (validateErr) return
  6. const [err] = await to(this.$api.xx())
  7. if (err) return this.$message.error('提交失败')
  8. this.$message.success('提交成功')
  9. }
  10. }

改造后

  1. // utils/decorator.js
  2. /**
  3. * 表单校验
  4. * @param {String} formElKey - 表单el
  5. */
  6. export const formValidation = (formElKey = 'formEl') => {
  7. return (target, name, descriptor) => {
  8. const method = descriptor.value
  9. descriptor.value = async function() {
  10. const _this = this._isVue ? this : target
  11. const isValidate = _this[formElKey]?.validate
  12. if (isValidate) {
  13. const [, res] = await to(isValidate())
  14. if (!res) return false
  15. }
  16. return method.apply(_this, arguments)
  17. }
  18. }
  19. }
  1. import { formValidation } from '@utils/decorator.js'
  2. methods: {
  3. @formValidation('formEl')
  4. handleSubmit() {
  5. const [err] = await to(this.$api.xx())
  6. if (err) return this.$message.error('提交失败')
  7. this.$message.success('提交成功')
  8. }
  9. }

是不是开始觉得有思路了,好像很多东西都可以用装饰器了,继续往下看,持续释放大招。

Element ui 的 异步messageBox

发现官方的异步messageBox写法,代码量很多,而我们大多数是异步方法不同,其余都基本一直,即使想变个title或者content,也可以改个参数就好了,请步移此处查看。

改造前

  1. methods: {
  2. handleSave() {
  3. this.$confirm('确定执行批量删除用户吗?', '批量删除用户', {
  4. dangerouslyUseHTMLString: true,
  5. distinguishCancelAndClose: true,
  6. confirmButtonText: '删除',
  7. beforeClose: async (action, instance, done) => {
  8. if (action !== 'confirm') return done()
  9. instance.confirmButtonText = '执行中...'
  10. const [err] = await this.$to(this.$api.delUser({ ids }))
  11. if (err) return done()
  12. this.$message.success('批量删除成功!')
  13. done()
  14. }
  15. })
  16. }
  17. }

改造后

  1. // utils/decorator.js
  2. /**
  3. * 确认框
  4. * @param {String} title - 标题
  5. * @param {String} concent - 内容
  6. * @param {String} confirmButtonText - 确认按钮名称
  7. * @returns
  8. */
  9. export const confirm = (title, concent, confirmButtonText = '确定') => {
  10. return (target, name, descriptor) => {
  11. const method = descriptor.value
  12. descriptor.value = function (...args) {
  13. const isUseFunction = (key) => toType(key, 'Function') ? key(...args) : key
  14. const _this = this._isVue ? this : target
  15. const _title = isUseFunction(title)
  16. const _concent = isUseFunction(concent)
  17. return _this.$confirm(_concent, _title, {
  18. dangerouslyUseHTMLString: true,
  19. distinguishCancelAndClose: true,
  20. confirmButtonText: confirmButtonText,
  21. beforeClose: async (action, instance, done) => {
  22. if (action !== 'confirm') return done()
  23. instance.confirmButtonText = '执行中...'
  24. const [err] = await to(method.call(_this, ...args), instance, 'confirmButtonLoading')
  25. if (err) return console.error(err)
  26. done()
  27. }
  28. })
  29. }
  30. }
  31. }
  1. import { formValidation } from '@utils/decorator.js'
  2. methods: {
  3. @confirm('批量删除用户', '确定执行批量删除用户吗?', '删除')
  4. async handleDel(ids) {
  5. const [err] = await this.$to(this.$api.delUser({ ids }))
  6. if (err) return
  7. this.$message.success('批量删除成功!')
  8. this.getData()
  9. }
  10. }

运行效果
2022/01/24 *【✨vue2.6 装饰器,用了就会真香🤣】 - 图2

防抖

  1. // utils/decorator.js
  2. /**
  3. * 防抖,连续操作时,只在最后一次触发
  4. * @export
  5. * @param {Function} fun - 运行函数
  6. * @param {Number} wait - 延迟时间
  7. * @returns
  8. */
  9. export function debounce(wait) {
  10. return function(target, name, descriptor) {
  11. const fn = descriptor.value
  12. let timer = null
  13. descriptor.value = function() {
  14. const _this = this._isVue ? this : target
  15. clearTimeout(timer)
  16. timer = setTimeout(() => {
  17. fn.apply(_this, arguments)
  18. }, wait)
  19. }
  20. }
  21. }
  1. import { debounce } from '@utils/decorator.js'
  2. methods: {
  3. @debounce(500)
  4. handleSubmit() {
  5. console.log('试试就试试')
  6. }
  7. }

节流

  1. // utils/decorator.js
  2. /**
  3. * 节流,一定时间内,只能触发一次操作
  4. * @export
  5. * @param {Function} fn - 运行函数
  6. * @param {Number} wait - 延迟时间
  7. * @returns
  8. */
  9. export function throttle(wait) {
  10. return function(target, name, descriptor) {
  11. const fn = descriptor.value
  12. let canRun = true
  13. descriptor.value = function() {
  14. const _this = this._isVue ? this : target
  15. if (!canRun) return
  16. fn.apply(_this, arguments)
  17. canRun = false
  18. setTimeout(() => {
  19. canRun = true
  20. }, wait)
  21. }
  22. }
  23. }
  1. import { throttle } from '@utils/decorator.js'
  2. methods: {
  3. @throttle(500)
  4. handleSubmit() {
  5. console.log('试试就试试')
  6. }
  7. }

缓存计算结果

  1. /**
  2. * 缓存计算结果
  3. * @export
  4. * @param {Function} fn
  5. * @returns
  6. */
  7. export function cached() {
  8. return function(target, name, descriptor) {
  9. const method = descriptor.value
  10. const cache = new Map()
  11. descriptor.value = function() {
  12. const _this = this._isVue ? this : target
  13. const key = JSON.stringify(arguments)
  14. if (!cache.has(key)) {
  15. cache.set(key, method.apply(_this, arguments))
  16. }
  17. return cache.get(key)
  18. }
  19. }
  20. }
  1. import { cached } from '@utils/decorator.js'
  2. methods: {
  3. @cached()
  4. handleSubmit(a, b, c) {
  5. console.log('试试就试试')
  6. return a + b + c
  7. }
  8. }

开启关闭loading

  1. /**
  2. * 自动开启loading
  3. * @export
  4. * @param {string} [loadingKey='loading']
  5. * @returns
  6. */
  7. export function autoSwitch(loadingKey = 'loading') {
  8. return function(target, name, descriptor) {
  9. const method = descriptor.value
  10. descriptor.value = async function() {
  11. const _this = this._isVue ? this : target
  12. _this[loadingKey] = true // 开启
  13. const [err, result] = await to(method.apply(_this, arguments))
  14. _this[loadingKey] = false // 关闭
  15. return err || result
  16. }
  17. }
  18. }
  1. import { autoSwitch } from '@utils/decorator.js'
  2. methods: {
  3. @autoSwitch('loading')
  4. async handleSubmit() {
  5. try {
  6. const res = this.$api.xx()
  7. console.log(res)
  8. } catch (error) {
  9. console.log(error)
  10. }
  11. }
  12. }