简介
防抖和节流是用在对一些存在大量无意义频繁调用事件问题的一种优化方式,如:
- 浏览器窗口滚动事件
- 鼠标滑动事件
- 输入事件
常用在的业务场景
- 页面回滚
- 即时搜索
区别
防抖和节流是有区别的,如下:
- 防抖:防抖是规定了事件调用后,在某个时间段后事件才执行,如果那个时间段未到之前,如果又调用了事件,则重新开始计时
- 节流:节流是规定了事件调用后,在某个时间段后事件才执行,如果那个时间段未到之前,如果又调用了事件,则不做任何处理
注意
什么时候用防抖,什么时候用节流,要看产品的需要
防抖(debounce)
1,效果
防抖的使用效果就是,当你不断地触发事件,事件是不会执行的,只有当你停下来,等一下,事件会就执行一次
2,实现
function debounce(fn, time) {
let timeout;
return () => {
if(timeout){ clearTimeout(timeout) };
timeout = setTimeout(_=>{
fn.call(this) //调用业务代码
}, time);
};
}
节流(throttle)
1,效果
节流的效果就是,当你不停地触发事件,事件并不会频繁地执行,而是隔一段时间执行一次,隔一段时间执行一次
2,实现
function throttle(fn, time) {
let timeout;
return () => {
if(!timeout){
timeout = setTimeout(_=>{
fn.call(this); //调用业务代码
timeout = null;
}, time)
}
};
}
不用timeout
的用法,利用了时间间隔
function throttle(fn, time) {
let prev = 0;
return () => {
let now = new Date().getTime()
if( now - prev > time){
timeout = setTimeout(_=>{
fn.call(this);
prev = now;
}, time)
}
};
}
案例
1,准备
比如有一个输入框和一个展示面板,如下:
<div>
<input type="text" id="input">
<h1 id='display'>
</h1>
</div>
我们将为输入框绑定一个输入事件,之后把输入的内容展示到展示面板里
document.getElementById('input').oninput = (e) => {
document.getElementById('display').textContent = e.target.value
}
在页面里可以看到
也就是说我们每输入一次,我们就调用并执行了一次事件,如果这个事件不是向展示框里显示数据,而是向后台服务器发送AJAX,那么是不是在我们输入完之前就已经向后台发送了好几个没有意义的数据了呢?为了避免这种情况,我们准备来优化下。我们想想,这种情况用防抖好还是节流好呢?我认为是防抖好一点,因为这个情况用防抖的话,可以达到只要我还在输入,事件就不会执行的效果,而节流的话,有可能我在输入但是还没输入完,事件就已经执行了。接下来我们两种情况都试试
2,防抖案例
我们把上面的js代码改成下面这个
// 防抖函数
function debounce(fn, time) {
let timeout; //定时器
return () => {
// 如果还在计时则清除,之后从新设置定时
if(timeout){ clearTimeout(timeout) };
timeout = setTimeout(_=>{ fn.call(this) }, time); //几秒后执行业务函数
};
}
// 业务函数,展示我们的输入
let fn = () => {
document.getElementById('display').textContent = document.getElementById('input').value
}
document.getElementById('input').oninput = debounce(fn, 2000) // 设置为2秒
上面的代码的大致意思就是当用户第一次输入一个字符时,就会设置一个2秒的定时器,定时器执行的是我们相要的业务函数,这里就是把输入内容展示出来,当用户再一次输入字符时,如果2秒倒计时未结束,则倒计时从新回到2秒,只有当用户输入完停下来,再等2秒,事件才会执行。
大致效果如下,假如我们需要输入的字符是1234
我们可以看到,在我输入1
,2
,3
的过程中,展示面板并不会显示数据,这是因为我们的业务函数未执行,只有当我输入完1234
然后等2秒后,事件才会触发。这样,如果这个业务函数是向后台服务器发AJAX请求,那可想而知可以为服务端减少多少压力
3,节流案例
现在看看节流操作的话会怎样,把js代码改成如下
// 节流函数
function throttle(fn, time) {
let timeout; //定时器
return () => {
// 如果定时结束了才增加定时,但是没结束的话不做任何操作
if(!timeout){
timeout = setTimeout(_=>{
fn.call(this); // 定时结束后执行业务函数
timeout = null; // 定时结束后清除定时器
}, time)}
};
}
// 业务函数
let fn = () => {
document.getElementById('display').textContent = document.getElementById('input').value
}
document.getElementById('input').oninput = throttle(fn, 2000)
上面的代码意思就是当事件触发后,设置了一个2秒的定时器,在倒计时2秒结束之前,如果再次触发事件,则不做任何操作,只有当2秒倒计时结束也就是执行一次业务函数之后,如果再次触发事件,就就会再设置一个2秒的定时器。
大概效果如下,假如我们需要输入的字符是1234
也就是说,在我输入的第一个字符时,就会有一个2秒的定时器,在两秒结束之前,如果我继续输入,则什么都不发生,当两秒结束后就会执行一次业务函数,如果之后我继续输入,则又会增加一个定时器,也是2秒后触发业务函数
从效果可以看到,在我还没输完1234
的时候,业务函数就已经触发了,所以我感受节流不适合这个情况
总结
1,防抖
当用户操作时,每次都需要计时,如果之前有定时器,就先清除定时器后再开始计时
2,节流
当用户操作时,如果定时器还在倒计时没有结束,就直接return
,当定时器倒计时结束后执行函数后,把自己清掉