防抖节流必要性
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…