vue项目中使用全局自定义指令实现防抖和节流,防止频繁调用接口
一、防抖和节流的思想
防抖(Dedouncing):在一定时间范围内,如果同一事件多次被触发,只执行最后一次操作。也就是说,在防抖时间内持续触发某个事件,只有在事件触发停止一段时间后才会真正执行。打个比方:好比玩王者荣耀,你不断地点击回城键,但是只会执行最后一次回城事件。
防抖的应用场景很多,例如搜索框输入时进行搜索、窗口大小调整等,每次触发可能导致大量的资源被占用,使用防抖可以减少函数处理次数,提高性能。
具体实现方式:当事件持续触发时,一个定时器会重新开始计时,只有在定时器间隔超过阀值时,该事件才会得到响应。
节流(Throttling):在一定的时间间隔内,无论事件触发了多少次,都只执行第一次触发的事件。也就是说,每隔一段时间执行一次操作,而不管这段时间内事件触发了多少次。打个比方:好比玩王者荣耀,你不断地点击三个技能,但是技能是需要冷却时间的,只有在技能冷却时间到了,你才可以重新使用技能。**
节流的应用场景也很多,**例如鼠标滚动事件、页面元素的拖拽操作、异步请求等,在这些操作中,连续触发事件可能会导致大量的资源被占用,使用节流可以减小函数的处理频率,提高性能。
具体实现方式:设置一个定时器,在规定的时间内只执行一次操作。如果该时间内事件再次被触发,则不会做任何操作,等规定的时间到了,只执行第一次触发的事件。
二、防抖和节流的代码
1.防抖
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>防抖</title>
</head>
<body>
<button>点击</button>
<script>
let btn = document.querySelector('button')
function debounce(func, delay) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
func.apply(this, arguments);//执行回调函数,apply()改变this,指向事件源(button)
timer = null;
}, delay);
};
}
btn.addEventListener('click', debounce(function (e) {
console.log('点累了吗?累了,过了300毫秒我要执行了!',e.target);
}, 300))
</script>
</body>
</html>
其中 func 是需要进行防抖操作的函数,func=>function (e) { console.log('可以执行了!',e.target); }
,delay 是设置的防抖间隔时间。在该函数内部,每当事件被重复触发时都会重新计时,只有不再触发且延迟时间后才会真正执行最后一次触发的事件。
实际应用中,可以将需要防抖的函数作为参数传递给触发的函数,例如:
btn.addEventListener('click', debounce(function (e) {
console.log('点累了吗?累了,过了300毫秒我要执行了!',e.target);
}, 300))
2.节流
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>节流</title>
</head>
<body>
<button>获取验证码</button>
<script>
let btn = document.querySelector('button')
function throttle(fn, deplay) {
let timer = null
return function () {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, arguments)
timer=null
}, deplay)
}
}
}
btn.addEventListener('click', throttle(function (e) {
console.log('等2s我再执行!',e.target);
}, 2000))
</script>
</body>
</html>
这里 fun 同样是需要进行节流操作的函数,deplay是规定的时间间隔。创建一个变量 timer 用于记录上一次执行的定时器ID;返回一个匿名函数,在该函数中使用 if (!timer) 判断是否存在上一次定时器ID;如果不存在,则在当前上下文中触发定时器,并设定定时器计时结束后执行要进行节流控制的目标函数;在定时器完成之后,将上一次定时器ID清空并返回函数,以便下一轮调用。
使用方法同样是将需要节流的函数作为参数传递给触发的函数:
btn.addEventListener('click', throttle(function (e) {
console.log('等2s我再执行!',e.target);
}, 2000))
三、vue项目中使用防抖和节流
我这里只做按钮的防抖功能,防止多次调用接口
**1.**首先在项目中创建一个directive.js文件,放在哪根据你的项目具体情况来决定,我一般是放在utils中。
import Vue from 'vue'
/*
按钮防抖自定义指令
*/
Vue.directive('debounce', {
inserted: function (el, binding) {
let timer = null
el.addEventListener('click', () => {
if (timer) {
clearTimeout(timer)
timer = null
}
timer = setTimeout(() => {
binding.value()
}, parseInt(binding.arg || 500))
})
}
})
解释:
1.binding.value()
binding.value()是只有这样引入<el-button v-debounce:500="buttonDebounce">按钮</el-button>
才会有这个value()方法,如果你只是单独地引入<el-button v-debounce @click="buttonDebounce">按钮</el-button>
,则不会有value()这个方法的,binding.value()这个方法是我们绑定的点击事件。
2.binding.arg
binding.arg接收<el-button v-debounce:500="buttonDebounce">按钮</el-button>
传过来的500,如果不传,这样引入的话<el-button v-debounce @click="buttonDebounce">按钮</el-button>
,binding.arg的值的undefined,有值的话类型是一个string类型,为什么要加parseInt,因为binding.arg的值是一个string类型,所以转换一下。大家也可以尝试打印一下看看。
2.在main.js中引入directive.js文件就可以了
import '@/utils/directive.js'
在 template 模板中使用 v-debounce 指令,绑定防抖函数
<el-button v-debounce:500="buttonDebounce">按钮</el-button>
其中:500是我们用来自定义时间参数的,对应directive.js中binding.arg,表示设置防抖时间为500毫秒。可以根据需要进行调整。
这样,点击按钮时就会触发 buttonDebounce 方法,并进行防抖操作,从而达到了防抖的效果。可以将这个自定义指令封装成一个单独文件,在需要使用的地方引入即可,方便复用。