防抖函数

废话少说,先上一个防抖函数。
关键是如何应用到vue项目中。

/**
 * @desc 函数防抖
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param immediate true 表立即执行,false 表非立即执行
 */
export function debounce (func, wait, immediate = true) {
  let timeout
  return function () {
    if (timeout) clearTimeout(timeout)
    if (immediate) {
      var callNow = !timeout
      timeout = setTimeout(() => {
        timeout = null
      }, wait)
      if (callNow) func.apply(this, arguments)
    } else {
      timeout = setTimeout(function () {
        func.apply(context, args)
      }, wait)
    }
  }
}

应用方式一:直接import到每个组件单独使用

1、在每个需要应用debounce 函数的组件中,import导入debounce 函数,在这里插入图片描述
2、然后在methods中,使用debounce函数封装一下目标函数。比如这里我们的目标是执行clickTest方法,要么直接将clickTest方法中的操作放到debounce函数的回调函数中执行,要么直接将整个clickTest方法放进来执行。
需要注意的是,这里debounce函数中的回调函数,一定不能写成箭头函数的形式(我知道你们es6玩得666),因为箭头函数绑定this,导致this为undefined。写成普通函数就可以直接调用vue实例中的方法了。

  methods: {
    debounceClickTest: debounce(function () {
      this.clickTest()
    }, 1000),
    clickTest () {
      console.log(this.form.uname, this.form.upwd)
    }
  }

3、最后在html部分直接调用debounceClickTest,
在这里插入图片描述

这种方法,并不推荐使用。除非项目中只有极个别地方,需要应用防抖。而且除非是一开始就按照这样处理,否则等项目写完了,还得一个个的将需要应用防抖的函数包裹到debounce函数中,对项目代码的破坏性太大。

应用方式二:函数式组件实现防抖。

1、debounce.js

import Vue from 'vue'
const debounce = (func, time, ctx, immediate = true) => {
  let timeout
  return function (...params) {
    if (timeout) clearTimeout(timeout)
    if (immediate) {
      var callNow = !timeout
      timeout = setTimeout(() => {
        timeout = null
      }, time)
      if (callNow) func.apply(ctx, params)
    } else {
      timeout = setTimeout(function () {
        func.apply(ctx, params)
      }, time)
    }
  }
}

Vue.component('Debounce', {
  abstract: true,
  props: ['time', 'events', 'immediate'],
  created () {
    this.eventKeys = this.events && this.events.split(',')
    this.originMap = {}
    this.debouncedMap = {}
  },
  render () {
    const vnode = this.$slots.default[0]
    // 如果默认没有传 events,则对所有绑定事件加上防抖
    if (!this.eventKeys) {
      this.eventKeys = Object.keys(vnode.data.on)
    }
    this.eventKeys.forEach((key) => {
      const target = vnode.data.on[key]
      if (target === this.originMap[key] && this.debouncedMap[key]) {
        vnode.data.on[key] = this.debouncedMap[key]
      } else if (target) {
        this.originMap[key] = target
        this.debouncedMap[key] = debounce(target, this.time, vnode, this.immediate)
        vnode.data.on[key] = this.debouncedMap[key]
      }
    })
    return vnode
  }
})

Debounce组件可以接受time,events,immediate 这3个参数,其中time是必填的,events和immediate是选填的。
events不填则默认所有事件应用防抖。
immediate不填,则默认为true,即立即执行再防抖。

在render函数中,Debounce组件修改了子VNode的事件,再将其返回回去。

再看看组件的应用方法,先在main.js中引入

import '@/functionComponent/debounce'

然后在需要应用防抖组件的位置,直接使用函数式组件。

    <Debounce :time="1000">
      <button @click="func_component_test">函数式组件防抖测试</button>
    </Debounce>
    <Debounce :time="1000" events="click">
      <button @click="func_component_test">函数式组件防抖测试</button>
    </Debounce>
    <Debounce :time="1000" events="click" :immediate="false">
      <button @click="func_component_test">函数式组件防抖测试</button>
    </Debounce>

参考链接:
Vue实现函数防抖组件 https://juejin.im/post/5c2dc7a9e51d4573c8491e77#heading-0

不过这种方法,还是觉得稍显麻烦。如果多个位置应用防抖,那么就需要写非常多处<debounce></debounce>组件,但是对于学习函数式组件来说确实是一个不错的思路。

应用方法三:使用自定义指令封装防抖函数。

本次项目中,使用的就是这种方法。通过注册全局自定义指令,在需要应用防抖的地方,替换掉@click,这样对代码的修改比较少。
directive.js

import Vue from 'vue'
import { debounce } from '@/utils/common'
// 防抖自定义指令
Vue.directive('debounce', {
  bind (el, binding) {
    const executeFunction = debounce(binding.value, 1000)
    el.addEventListener('click', executeFunction)
  }
})

应用结果

    <button v-debounce="login">登陆</button>
    <br>
    <button v-debounce="getCount">自定义指定防抖测试</button>

节流函数的应用方式同理,这里就不赘述。

Logo

前往低代码交流专区

更多推荐