防抖节流之-Vue2.x自定义指令

1,313 阅读3分钟

防抖节流必要性

1、事件频繁地调用,客户端承载过大的负担,导致不友好的用户体验;
2、用户在提交表单时,短时间内多次点击,希望能够只进行一次提交操作,此时防抖节流就很有必要;

常见的防抖节流

在写此文之前,也有很多大佬给出防抖节流的方法或方式,有幸在实际操作中也用到。

防抖函数(debounce)

在触发事件时,设置一个定时器在指定(delay)时间后执行,如果在指定时间内容多次触发,则会清除前一个定时器,重新定义定时器执行。

    function debounce(fn, delay) {
      let timer = null;
      return (...args) => {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
          fn.apply(this,args);
        }, delay);
      }
    }

节流函数(throttle)

触发事件后,指定时间(delay)内无法连续调用,只有上一次函数执行后,过了指定时间,才能进行下一次的函数调用。

function throttle(fn, delay){
   let last = null,
   return function () {
      const now = + Date.now()
      if(now > last + delay){
      	 last = now
      	 fn.apply(this,args)
      }	  
   }
}

使用Vue自定义指令,实现防抖节流

Vue中提供了全局的API directive 来 注册或获取全局指令。感兴趣的童鞋可以参考:Vue.directive

下面我们进入正题,写一个自定义指令来实现防抖节流:

防抖指令

Vue.directive('debounce', {
  // el 指令所绑定的元素,可以用来直接操作 DOM 。
  inserted: function (el, binding, vnode) {
    let { formatEl, formatBinding, formatVnode } = formatDebounceThrottleParams('debounce',
                                                            el, binding, vnode);
    formatEl["timeCall"] = null;
    switch (el.type) {
      case "click":
        // 点击事件
        formatEl.addEventListener('click', () => {
          if (formatEl.timeCall) clearTimeout(formatEl.timeCall);
          let callNow = !el.timeCall;
          formatEl.timeCall = setTimeout(() => {
            formatEl.timeCall = null;
          }, formatEl.time)
          if (callNow) formatEl.callback(...formatEl.params);
        })
        break;
      default:
        // 默认为点击事件
        formatEl.addEventListener('click', () => {
          if (formatEl.timeCall) clearTimeout(formatEl.timeCall);
          let callNow = !formatEl.timeCall;
          formatEl.timeCall = setTimeout(() => {
            formatEl.timeCall = null;
          }, formatEl.time)
          if (callNow) formatEl.callback(...formatEl.params);
        })
        break;
    }
  },
  // 所在组件的 VNode 更新时调用
  update: function (el, binding, vnode) {
    let { formatEl, formatBinding, formatVnode } = formatDebounceThrottleParams('debounce',
                                                            el, binding, vnode);
    el = formatEl;
    binding = formatBinding;
    vnode = formatVnode;
  },
});

使用方法:

<div
    v-debounce:callback="[arg1, arg2, ...]"
    :debounceTime="500"
    :debounceType="'click'"
>测试防抖指令</div>

之所以以 v-debounce:callback="[arg1, arg2, ...]" 形式使用,请移步Vue官网中的#动态指令参数 查看。

节流指令

Vue.directive('throttle', {
  // el 指令所绑定的元素,可以用来直接操作 DOM 。
  inserted: function (el, binding, vnode) {
    let { formatEl, formatBinding, formatVnode } = formatDebounceThrottleParams('throttle',
                                                            el, binding, vnode);
    formatEl["timeCall"] = null;
    switch (formatEl.type) {
      case "click":
        // 点击事件
        formatEl.addEventListener('click', () => {
          const nowTime = new Date().getTime();
          if (!formatEl.preTime || nowTime - formatEl.preTime > formatEl.time) {
            formatEl.preTime = nowTime;
            formatEl.callback(...formatEl.params);
          }
        })
        break;
      default :
        // 默认为点击事件
        formatEl.addEventListener('click', () => {
          const nowTime = new Date().getTime();
          if (!formatEl.preTime || nowTime - formatEl.preTime > formatEl.time) {
            formatEl.preTime = nowTime;
            formatEl.callback(...formatEl.params);
          }
        })
    }
  },
  update: function (el, binding, vnode) {
    let { formatEl, formatBinding, formatVnode } = formatDebounceThrottleParams('throttle',
                                                            el, binding, vnode);
    el = formatEl;
    binding = formatBinding;
    vnode = formatVnode;
  }
});

使用方法:

<div
    v-throttle:callback="[arg1, arg2, ...]"
    :throttleTime="500"
    :throttleType="'click'"
>测试节流指令</div>

上面两个指令中使用到的公共方法:formatDebounceThrottleParams

const formatDebounceThrottleParams = function (type, el, binding, vnode) {
  el.callback = vnode.context[binding.arg];
  // 防抖节流时间,由传入的 type 加上 Time 组成的字符串
  el.time = (vnode.data.attrs && vnode.data.attrs[`${ type }Time`]) ?
                  Number(vnode.data.attrs[`${ type }Time`]) : 500;
  // 防抖节流事件类型,由传入的 type 加上 Type 组成的字符串
  el.type = (vnode.data.attrs && vnode.data.attrs[`${ type }Type`]) ?
                  vnode.data.attrs[`${ type }Type`] : "click";
  el.params = Array.isArray(binding.value) ? binding.value : [];
  if (!Array.isArray(binding.value) && !!binding.value) {
    throw `使用${ type }指令时,传递参数不是 数组 类型`
  }
  return {
    formatEl: el,
    formatBinding: binding,
    formatVnode: vnode
  }
}

总结

本文中使用了Vue的自定义指令指令动态参数等来实现防抖节流方法,有了这两个指令,在项目中就可以随时进行防抖节流。
因为在项目中多处已有函数需要进行防抖节流,本着不想过多修改原本执行的函数和快速实现此功能,于是乎就使用了自定义指令来实现。
本文中如若出现纰漏之处,望各位童鞋积极指出 [抱拳] ;希望改文章能触发您的灵感,使用自定义指令来解决实际问题。
-------源码地址-------
最后附上源码地址: github.com/Prexxxxx/de…