3

三步带你玩转前端Vue装饰器

 2 years ago
source link: https://segmentfault.com/a/1190000040893884
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
  • 什么是装饰器
  • 装饰器怎么封装
  • 装饰器能干啥

1、什么是装饰器

看个例子就懂了eg:
正常开发是这样的:
    1、先定义节流方法:
        methods: {
          throttle (func, delay) {            
              var timer = null;            
              return function() {                
                  var context = this;               
                  var args = arguments;                
                  if (!timer) {                    
                      timer = setTimeout(function() {             
                          func.apply(context, args);               
                          timer = null;                    
                      }, delay);                
                  }            
              }        
          }    
        }
        2、然后执行A方法:
        methods: {
             a() {
                this.throttle(()=>{
                 //执行业务逻辑
                }, 400)
            }
        }

反正就是各种嵌套,看起来代码很脓肿,接下来看看【装饰器】怎么写↓

//使用装饰器过后的写法
import { throttle} from "@/utils/decorator";

methods: {
  @throttle(400)  // 装饰器(节流)  
  a() {
    // 执行业务逻辑
    //此时会发现点击效果跟上面写法一样
    console.log('执行业务')
  },
}

现在看到的写法是不是凉快了很多,没有多层嵌套

2、装饰器怎么封装

    1、在工具文件创建decorator.js
    // utils/decorator.js
    /**
     * 节流,一定时间内,只能触发一次操作
     * @export
     * @param {Function} fn - 运行函数
     * @param {Number} wait - 延迟时间
     * @returns
     */
    export function throttle(wait = 2000) {
      //返回值:被传递给函数的对象。    
      return function(target, name, descriptor) {
        // @param target 类本身
        // @param name 装饰的属性(方法)名称
        // @param descriptor 属性(方法)的描述对象
        const fn = descriptor.value 
        let canRun = true
        descriptor.value = async function(...args) {
          //具体的装饰器业务在这里面编写    
          if (!canRun) return
          await fn.apply(this, args) // 执行业务下的方法
          canRun = false
          setTimeout(() => {
            canRun = true
          }, wait)
        }
      }
    }
    2、在业务板块里面声明使用
       methods: {
          @throttle(400)  // 装饰器(节流)  
          a() {
            // 执行业务逻辑
            //此时会发现点击效果跟上面写法一样
            console.log('执行业务')
          },
        }
    //现在看到代码是不是就没有那么脓肿了,就一行指令

3、装饰器能干啥

现实开发中经常遇到节流,防抖,日志,按钮权限等等一些业务执行之前的拦截操作

以下是我平时使用的一些装饰器,希望对看到这里的你有帮助!

// utils/decorator.js
import { Dialog } from 'vant';

/**
 * loading 开关装饰器
 * @param {String} loading 当前页面控制开关的变量名字
 * @param {Function} errorCb 请求异常的回调 返回error 一般不用写
 * 如果 errorCb 为 function 为你绑定 this  如果是箭头函数 则第二个参数为this
 * @example
 * @loading('pageLoading',function(){that.demo = '123123'})
 * async getTable(){
 *  this.table =  this.$apis.demo()
 * }
 * @example
 * @loading('pageLoading',(error,that)=>{that.demo = '123123'})
 * async getTable(){
 *  this.table =  this.$apis.demo()
 * }
 */
export function loading (loading, errorCb = Function.prototype) {
  return function (target, name, descriptor) {
    const oldFn = descriptor.value;
    descriptor.value = async function (...args) {
      try {
        this[loading] = true;
        await oldFn.apply(this, args);
      } catch (error) {
        errorCb.call(this, error, this);
      } finally {
        this[loading] = false;
      }
    };
  };
}

/**
 * 日志注入
 * @export
 * @param {Function} fn - 运行函数
 * @param {data} 日志需要的参数
 * @returns
 */
export function log(data) {
  return function(target, name, descriptor) {
    const fn = descriptor.value;
    descriptor.value = async function(...args) {
      await logApi(data) // 自己的日志接口
      await fn.apply(this, args);
    }
  }
}

// utils/decorator.js
/**
 * 节流,一定时间内,只能触发一次操作
 * @export
 * @param {Function} fn - 运行函数
 * @param {Number} wait - 延迟时间
 * @returns
 */
export function throttle(wait= 2000) {
  return function(target, name, descriptor) {
    const fn = descriptor.value
    let canRun = true
    descriptor.value = async function(...args) {
      if (!canRun) return
      await fn.apply(this, args)
      canRun = false
      setTimeout(() => {
        canRun = true
      }, wait)
    }
  }
}
// utils/decorator.js
/**
 * 防抖,连续操作时,只在最后一次触发
 * @export
 * @param {Function} fun - 运行函数
 * @param {Number} wait - 延迟时间
 * @returns
 */
export function debounce(wait= 2000) {
  return function(target, name, descriptor) {
    const fn = descriptor.value
    let timer = null
    descriptor.value = function(...args) {
      const _this = this._isVue ? this : target
      clearTimeout(timer)
      timer = setTimeout(() => {
        fn.apply(_this, args)
      }, wait)
    }
  }
}
/**
 * 表单校验
 * @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)
    }
  }
}
// utils/decorator.js
/**
 * 确认框
 * @param {String} title - 标题
 * @param {String} concent - 内容
 * @param {String} confirmButtonText - 确认按钮名称
 * @returns
 */
export const alertDecorator = ({title = '提示', message = '请输入弹窗内容', confirmButtonText = '我知道了'}) => {
  return (target, name, descriptor) => {
    const fn = descriptor.value;
    descriptor.value = function (...args) {
        Dialog.alert({title, message, confirmButtonText}).then(() => {
          fn.apply(this, args);
        });
    }
  }
}



/**
 * 缓存计算结果
 * @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)
    }
  }
}
既然看到了这里,先收藏一下,如果实战划水时间提高了,别忘了回来点个赞哦

如果觉得有用,就分享给你的小伙伴吧!
接下来就是快乐的划水了 O(∩_∩)O哈哈~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK