前言
知识只有配合实践,吸收内化成自己的东西,才能在用到的时候举重若轻,挥洒自如。
自我提问
- 防抖和节流的使用场景是什么?使用这两个东西的目的是什么?
- 防抖和节流是什么意思?为什么叫这个名字?有什么区别?
使用场景
技术的诞生都是为了解决实际生活中遇到的问题的,互联网的诞生解决了空间距离造成的交流的阻碍。弄明白了某个知识诞生的背景,那么更能够领会这个知识点的内涵。
在前端开发中,我们经常会遇到一些浏览器事件触发频率很高的场景。
- 场景一:移动端网页中,经常在右下角会有一个返回顶部的按钮,并且这个按钮通常都是在页面向下滚动一定距离后才会出现的。那么如何知道页面已经向下滚动了多少距离呢?需要监听页面的"scroll"事件。代码如下,
window.addEventListener('scroll', () =>{
const scrollTop = document.body,scrollTop || document.documentElement.scrollTop;
console.log('页面滚动距离', scrollTop);
}, false)
在实际运行上面的代码之后,我们会发现一个问题,那就是scroll事件触发的频率太高了。使用鼠标滚轮稍微滚动一下,事件就会触发4、5次。这对于我们业务逻辑没有太大好处,反而损害了浏览器性能。
2. 场景二:输入框验证输入字符是一个很常见的需求,如果你的验证逻辑需要调用后端接口的话,那就比较耗费时间了。例如搜索框输入,每输入,都要调用API查询关联的词条下拉展示。这种情况下,我们不希望每次输入都要掉接口(因为有的时候输入的是拼音),而是在输入完全后再调用接口。
总结一下,上面提到的应用场景。都是事件触发频率过高,损害了浏览器性能。作为开发者,希望能够通过某种方式控制事件触发的评论,并且对业务逻辑没有影响。而这也是防抖和节流的目的———控制事件触发的频率。
防抖(debounce)
上面已经说了我们希望找到一个方法控制事件触发的频率,不同的开发者有着不同的解决问题的思路,其中两个经受实际检验得到大家认可的思路就是——防抖和节流。
基于上述场景,首先提出第一种思路:在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后:
- 如果在200ms内,事件再次触发,当前的计时取消,重新开始计时
- 如果在200ms内,事件没有触发,那么执行事件的处理函数
效果:指定时间内连续触发事件,只在最后一次事件触发结束后的指定时间之后,执行一次处理函数。
代码实现:
/* 防抖 */
function debounce(fn, delay) {
let timer;
return function() {
if (timer) clearTimeout(timer);
timer = setTimeout(fn, delay);
}
}
window.addEventListener('scroll', debounce(() => {
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('页面滚动距离', scrollTop);
},1000), false);
实际运行,可以发现只有在停止滚动1s后,才会执行处理函数。
节流(throttle)
在防抖的基础上继续思考,如果使用防抖的解决方案,那么会出现一个新问题:如果某个用户闲着无聊,一直不停的滚动页面,那么理论上是不会执行滚动事件的处理函数的。因为只有停止滚动1s后,才会执行处理函数。这样的结果是产品不想要的,产品希望即使用户不停的滚动页面,也能隔一段时间就能执行处理函数。类似技能冷却时间,冷却时间内不能使用。
那么我们可以给出思路:当事件第一次触发的时候,也是不立即执行函数,而是给出一个期限值比如200ms,开始计时,然后
- 在200ms内,再次触发的事件被全部忽略,计时结束后,执行一次函数,并且清理计时器。
- 200ms结束后,重新开始上述循环
效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
代码实现:
function throttle(fn, delay) {
let timer;
return function() {
if (!timer) {
timer = setTimeout(() => {
fn();
clearTimeout(timer);
timer = null;
}, delay)
}
}
}
window.addEventListener('scroll', throttle(() => {
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('页面滚动距离', scrollTop);
},1000), false);
总结:
防抖和节流是两种解决短时间事件触发频率过高问题的思路,防抖是动态的,只有当最后一次事件触发结束之后,才会执行处理函数。中间没有处理过函数,防止了页面"抖动",可以认为保证了过程的连续平滑。而节流是离散的,明确对时间进行了分割,在指定的时间块中只执行一次事件处理函数。