防抖函數
廢話少說,先上一個防抖函數。
關鍵是如何應用到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>
節流函數的應用方式同理,這裏就不贅述。