浏览器防抖和节流
鉴于在测试代码过程中滚动滚动条之后在控制台打印了一万条日志,本人决定好好学习一下防抖和节流,以下均为个人学习记录(这瓜不保熟),有错误欢迎指正
举个栗子
开发网站时,常常会有这样一个需求,网站右下角需要在滑动一定距离后出现一个”回到顶部“的按钮,方便用户操作。要实现这个功能,方法也很简单,监听浏览器的滚动事件,当滚动距离达到自己需要的距离时,让右下角的按钮出现,并固定定位在右下角,直接上关键代码。
function showTop () {
let sTop = document.body.scrollTop || document.documentElement.scrollTop
console.log('滚动条位置:' + sTop)
if (sTop >= '所需的距离') { /* 这里去触发出现按钮的事件以及设置样式 */}
}
window.onscroll = showTop
但是这样处理,也会造成另外一个问题——触发频率过高。
可以预见,每次滚动一点点,就会触发多次这个事件,消耗了浏览器的性能,这并不是我们想要的结果,这个时候就可以使用防抖和节流优化这种场景
防抖
为了处理上面遇到的问题,基本思路就是降低事件触发的频率,所以第一种方案就应运而生了,我们可以让事件在一段时间内只触发一次。简易的方案如下:当第一次触发事件时,我们不去立即触发事件处理函数,而是给出一段时间,这时候会有两种情况
- 若在这段时间内事件没有再次触发,那么就可以在时间结束后调用该处理函数
- 若在这段时间内事件再次触发了,那么重新开始计时,在这段时间中观察后续事件是否触发
听起来是不是很像定时器的功能呢?没错,就是使用定时器。通过计时器,我们就可以让事件在一定时间之后触发一次。借助参考资料大佬的思路,这里使用闭包来举例
function debounce(fn,delay){
let timer = null // 只执行一次
return function() {
if(timer){ // 多次触发定时器时先清除掉上一个
clearTimeout(timer)
}
timer = setTimeout(fn,delay)
}
}
// 实际的处理事件
function showTop () {
var sTop = document.body.scrollTop || document.documentElement.scrollTop
console.log('滚动条位置:' + sTop)
if (sTop >= '所需的距离') { /* 这里去触发出现按钮的事件以及设置样式 */}
}
window.onscroll = debounce(showTop,2000) // 设置间隔
执行代码后我们会发现,原来小小拖动就造成一万行打印的现象,变成了只打印一行,执行实际结果是进行滑动2000ms后,控制台才会打印出当前距离顶部的滑动距离,且只打印一次,这其实就实现了防抖,现在可以结合例子来理解防抖的含义了:
- 对于短时间连续触发的事件,防抖的含义就是让某个时间期限内,事件处理函数只触发一次
需要注意的是,上面这个例子,实际上滚动过程中也不断触发了debounce函数,但是相比真正的处理事件showTop,防抖函数的处理和消耗要少得多,所以也达到了优化的效果。
节流
上面的防抖已经初步达到了减少滑动处理事件的频率,但是事情就这样结束了吗?
真正试着去运行上面这段代码的话,会发现存在一个现象,你如果一直拖拽滚动条而不放开(别问为什么,问就是需求),控制台是永远不会打印出东西的,这是因为定时器一直没有结束(多次触发防抖时会清除掉上一次的定时器并开启另一个定时器)。这对于某些业务场景是不合适的,所以还需要考虑另外一种方案,比如能不能在某段时间内触发一次后就禁止触发,等过了这段时间后再恢复可触发状态呢?事实上是可以的,而且只需要在防抖的代码上进行一点修改就可以达到效果,如下:
function throttle(fn,delay){
let flag = true // 只执行一次
return function() {
if(!flag){ // 处于已生效状态则不触发
return false;
}
flag = false // 开始生效
timer = setTimeout(() => {
fn()
flag = true // 恢复可触发状态
}, delay)
}
}
// 实际的处理事件
function showTop () {
var sTop = document.body.scrollTop || document.documentElement.scrollTop
console.log('滚动条位置:' + sTop)
if (sTop >= '所需的距离') { /* 这里去触发出现按钮的事件以及设置样式 */}
}
window.onscroll = throttle(showTop,2000) // 设置间隔
上面的代码的结果,就变成了一直拖着滚动条时,控制台会以2000ms为间隔周期性输出结果,而这个就是节流的一种实现方式。现在可以给出节流的定义:
- 对于连续触发的事件,节流的含义就是让事件处理函数在一定的时间间隔后周期性触发
实际上,这只是节流的一种实现方式,不过只要掌握了节流的概念就不难写出相关的处理,这里就不一一列举
总结
防抖和节流都是为了减低浏览器触发事件的频率,提高利用率的方式,具体使用哪一种需要依据业务场景来定,比如搜索框的联想可以设定间隔一定时间后触发一次搜索、而页面重绘可以在最后进行触发,这里就分别可以用上节流和防抖。
参考资料
参考链接
感谢链接中的文章给予的参考,本文纯根据个人需求进行截取摘录以防工作中需要,如要更详尽的了解相关内容,可点击参考链接