防抖(節流)函數在Vue項目中的應用。


防抖函數

廢話少說,先上一個防抖函數。
關鍵是如何應用到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>

節流函數的應用方式同理,這裏就不贅述。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章