防抖节流-从简单使用到函数封装

246 阅读3分钟

1. 简单用法,在函数体中直接写入

由于有些业务场景使用防抖节流封装成函数后,会出现函数嵌套这种令人恶心的代码,让人头皮发麻, 比如:一个保存功能按钮,你的ajax请求函数已经封装过一次,然后在组件中使用时又在外层套了一层函数,最后,在某个按键点击事件上调用时,还得在上面在包一层防抖节流函数。啊!!!!,有没有感觉代码就像加了confrim二次确认弹框

所以我们可以直接利用定时器,在该函数直接写语句代码写一个简单的防抖节流

  • 防抖 reactHooks为列
export default function Foo(){

    const timer=useRef(null)
    //点击保存按钮
    const handleClick=()=>{
        if(timer.current) clearTimeout(timer.current)// 清除上一次定时器
        
        timer.current=setTimeout(()=>{
           /* handleCick函数体内容功能代码
            ...
            ...
            */
        },500)
    }
    
    return (
        <div>
            <button onClick={handleCLick}>保存</button>
        </div>
    )
    
}

由上面的例子可以看出,再次点击按钮,只要上一次的定时器还存在就会清空上一个的定时器(包括里面的逻辑代码),从而新开一个定时器,如果用户在指定时间间隔连续点击,就会一直清空上一个的定时器,直到最后一次点击才触发。

  • 节流 reactHooks为例:
export default function Foo(){

    const timer=useRef(null)
    //点击保存按钮
    const handleClick=()=>{
        if(timer.current) return 
        /* handleClick函数体内容功能代码
           ...
           ...
         */
         
        timer.current=setTimeout(()=>{ 
            timer.current=null
            clearTimeout(lock.current);//执行完毕清除当前计时器
        },500)
    }
    
    return (
        <div>
            <button onClick={handleCLick}>保存</button>
        </div>
    )
    
}

由上面例子我们可以看出,只需定义一个定时器,他为空时就走下面的逻辑,并且开启定时器,500ms后又自动清空,当不为空时,就会return,终止下面的逻辑,

2. 防抖,节流函数封装

  • 防抖
const debounce=(fn,delay= 500)=>{
    let timer=null
    return function(...args){ 
        //清除上一次的定时器
        if(timer) clearTimeout(timer)
        timer=setTimeout(()=>{
             //箭头函数this指向上一级函数也就是调用者,无需修改this
             fn(...args)
        },delay)
    }
}

//使用
const foo= debounce((a,b,c)=>{
    console.log(a,b,c)
})
foo(1,2,3) //1,2,3

  • 防抖第一次立即执行 有些时候我们设置的防抖时间过长,用户第一次点击时也会延迟,可是我们并不想在第一次的时候延迟,怎么办呢? 这个时候我们可以增加第三个参数firstClick:Boolean当需要第一次执行时传入
const debounce=(fn,delay=500,firstClick=false)=>{
    let timer=null
   
    return function(...args){ 
        if(timer) clearTimeout(timer) //清除上一次的定时器
        if(firstClick){
            if(!timer){
                fn(...args)
            }
            //再开启定时器结束时赋值为null,防止每次点击都走第一次,不然会失去防抖效果
            timer=setTimeout(()=>{
                timer=null
            },delay)
        }else{
            timer=setTimeout(()=>{ //形成闭包保存timer
                fn(...args)
            },delay)
        }
        
    }
}

上面的代码修改之后的效果就变成了,用户第一次点击会立即执行,但是在定时器的的延时时间内,连续点击都会没有效果,总而言之就是只有第一次才会触发,后续点击都没效果,(可能有人会说这是节流,不!这不是节流,节流和防抖的本质区别:节流是用户连续点击只要规定的时间一到就会执行一次。而防抖是用户只要在规定的时间连续点击,就算连续点击一天,也只会执行一次

  • 节流
function throttle (fn, delay=500) {
    let timer=null
    return function (...args) {
        if (timer) return
        timer = setTimeout(() => {
           fn(...args) 
           timer = null
           clearTimeout(lock.current);//执行完毕清除当前计时器
        }, delay)
    }
}

从上面函数看出,节流函数相对于防抖函数就要简单一点了,直接在规定的时间检查定时器是否存在,存在就不触发,不存在就触发,所以效果就变成了,不管你连续点击多久,在指定时间间隔都会执行一次。