一个跨行前端的小白菜随笔 —— 防抖函数
在前端开发过程中,我们常遇到一些问题。比如在实现响应式布局时,我们需要根据浏览器窗口的宽度,设定不同的排版模式。或者是在使用scroll时,需要时刻监听函数。
但是在不断缩放浏览器窗口、不断拖动scroll时,会不断地触发它的监听函数。这样对我们浏览器性能并不友好。因此我们想让它在缩放窗口、拖动scroll时,不会触发函数,只有等操作完成约100ms后,才开始触发函数。
这里,我们用Vue来举一个简单的🌰。
//在mounted中,添加一个监听浏览器窗口的函数 mounted() { window.addEventListener('resize',()=>{ console.log('211'); }) }
拖动时,可以看到浏览器的情况

这里面有个问题,就是如何判定拖动停止?
为了搞清楚这个问题,我们不妨观察一下这个函数里面发生的情况:当拖动浏览器缩放时,函数里面的事件不断执行。那我们假设在里面设置一个计数器,当事件执行的时候,计算器+1,当计数器有超过100ms没有+1时,执行resize 事件。但这又显然进入了一个悖论,因为定时器执行,也会消耗浏览器性能,所以不可取。
那么换个思路,我们还是设置一个100ms的定时操作,如果后续要不断执行这个操作,那么我们就不断取消这个操作,总之只保留最终只执行一次这个操作。显然,需要取消操作,那就需要涉及到定时函数setTimeout()。另外,因为resize 事件肯定会不断执行,但事件里的操作 (console.log('211'))却涉及到取消、执行,说明resize事件内部之间有某种关系,这就涉及到闭包。
mounted() { window.addEventListener('resize', ()=>{ let Event = null //设定一个Event操作事件 return function (){ if (Event !== null){ //如果执行了操作事件,立马清空 clearTimeout(Event) } Event = setTimeout(()=>{ //重新再执行一次操作事件 console.log('211') },300) } }) }
不过。。。似乎结果并不理想

问题出在了resize事件的匿名函数身上,我们修改下
function handl(){ let Event = null return function (){ if (Event !== null){ clearTimeout(Event) } Event = setTimeout(()=>{ console.log('213') },300) } } window.addEventListener('resize', handl())
也可以改成这样 匿名函数的形式
window.addEventListener('resize',(() =>{
let Event = null
return function (){
if (Event !== null){
clearTimeout(Event)
}
Event = setTimeout(()=>{
console.log('213')
},300)
}
})())
问题解决了

从上面的函数中,我们可以提炼出一个防抖函数
function debounce(fn,time){ //防抖函数 let Event = null return function (){ if (Event !== null){ clearTimeout(Event) } Event = setTimeout(fn,time) } } window.addEventListener('resize', debounce(()=>{ console.log('121'); },300))

浙公网安备 33010602011771号