You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
importReact,{useEffect,useRef}from'react'importdebouncefrom'../../utils/debounce'importthrottlefrom'../../utils/throttle'importstylefrom'./index.scss'exportdefaultfunctionDemo(props){constinputElem1=useRef()constinputElem2=useRef()constinputElem3=useRef()useEffect(()=>{inputElem1.current.addEventListener('keyup',request)inputElem2.current.addEventListener('keyup',debounce(request,1000))inputElem3.current.addEventListener('keyup',throttle(request,3000))},[])functionrequest(event){const{ value }=event.targetconsole.log(`Http request: ${value}.`)}return(<divclassName={style.container}><divclassName={style.list}><labelhtmlFor="input1">普通输入框:</label><inputname="input1"ref={inputElem1}defaultValue=""/></div><divclassName={style.list}><labelhtmlFor="input2">防抖输入框:</label><inputname="input2"ref={inputElem2}defaultValue=""/></div><divclassName={style.list}><labelhtmlFor="input3">节流输入框:</label><inputname="input3"ref={inputElem3}defaultValue=""/></div></div>)}
写在前面,实际项目应使用 Lodash 等主流工具库,它们经过社区的反复验证,肯定比自己写的要完善很多。
前言
相信无论在实际应用场景、亦或是面试,都会经常遇得到函数防抖、函数节流等,下面我们来聊一聊吧。
先放出一个示例:
以上 Demo 只有三个输入框,很简单。我给每个输入框绑定了一个
keyup
键盘事件,该事件执行会发起网络请求(为了更简洁,这里只是打印一下而已),而对应防抖、节流输入框则经过相应的处理。函数防抖(debounce)
如果我们在普通输入框快速键入
12345
,可以从控制台上的打印结果看到,它会发起 5 次网络请求(假设我们这个是一个简单的搜索引擎)。从实际场景考虑,如果每键入一个字符就立刻发起网络请求,去检索结果,这是非常影响体验的。假设我们限制为:用户在停止输入后 1s 后才发起网络请求。
要实现这样的需求,我们只有使用函数防抖即可。
什么是函数防抖?
概念:在一定时间间隔内,事件处理函数只会执行一次。若在该时间间隔内(多次)重新触发,则重新计时。
怎么理解?
a
后就停止输入了,那么网络请求会在停止键入操作的 1s 后发起。这个很好理解。b
后,若有所思地停了一会(这个时间在 1s 之内,假设为 800ms 吧),接着键入字母c
,之后就停止键入了。网络请求会发生在键入字母c
的 1s 后被发起,而不是键入字母b
之后的 1s 发起。因为函数防抖会在键入c
之后重新计时。函数防抖实现
实现思路:
首先,接收两个参数
func
(要防抖的函数,一般是事件回调函数)和wait
(需要延迟的时间间隔,单位毫秒)。然后func
在setTimeout
中执行,而setTimeout
的延迟时间就是wait
。而重新计时的话,则在每次触发的时候clearTimeout
即可实现。借助 ES6 的 Rest 参数和箭头函数语法,简化一下:
依次在对应输入框内键入
12345
,对比下防抖前后的结果:对比以上无防抖处理和防抖处理的结果,可以看到前者每键入一个字符都会执行回调函数,而后者则会在最后一次触发的 N 毫秒(即
wait
延迟时间)之后才会执行一次回调函数。再结合以上的“非立即执行”的防抖,完整方法如下:
当我们修改成:
从以下结果可以看到,当我在防抖输入框键入
12345
的时候,它会在键入1
时立刻发起一次网络请求,由于每个字符键入的时间间隔都在1s
之内,因此它只会在最后停止键入的1s
后才会发起网络请求。函数节流(throttle)
概念:在一定时间间隔内只会触发一次函数。若在该时间间隔内触发多次函数,只有第一次生效。
3.1 函数节流实现
3.2 函数节流优化
以上节流方法有个问题,假设节流控制间隔时间为 1s,若最后一次触发时间在 1.5s,则最后一次触发并不会执行。因此,需要在节流中嵌入防抖思想,以保证最后一次会被触发。
假设我将
clearTimeout()
放在了 2️⃣ 里面,而不是在外层。基于throttle(func, 1000)
考虑以下场景:当然,以上场景是在理想的状态,实际场景可能几乎碰不到这些边界。但从严谨的角度去看问题,应该也要考虑的。
我在节流输入框内,依次键入
1234567890
,可以看到:在键入字符1
时执行了回调;接着键入的234
、67
字符都属在上一个时间间隔内,因此无法执行回调。其中键入的90
字符应属于8
之后的 1s 周期之内,由于键入0
字符属于最后一次的非时间间隔内的触发动作,因此回调会在键入0
的 1s 后被执行。(可打印时间戳的形式,更精细地对比)防抖与节流
其实,函数防抖和函数节流都是为了防止某个时间段频繁触发某个事件。它俩在某个时间间隔内多次重复触发,都只会执行一次回调函数。区别在于函数防抖最后一次触发有效,而函数节流则是第一次触发有效。
而在上面,都对函数防抖和函数节流做了“拓展”,例如:
immediate
的参数,用于控制第一次是否执行回调。应用场景:
函数防抖(debounce)
函数节流(throttle)
如果还是不太明白 debounce 和 throttle 的差异,可以在以下这个页面,可视化体验。
参考
The text was updated successfully, but these errors were encountered: