引言
滚动页面、输入搜索词、拖动窗口大小,这些操作每秒可能触发几十上百次。如果每次触发都执行一段较重的逻辑,页面就会卡顿、掉帧。解决思路不是让逻辑变快,而是控制执行的频率。防抖和节流就是两种最常用的频率控制手段。这篇文章只讲这一个问题,把两者的区别、原理和适用场景讲清楚。
一、问题从哪来
很多事件是高频触发的。滚动事件在滚动过程中持续触发,输入事件每敲一个字符触发一次,窗口缩放每改变一点像素就触发一次。
如果每次触发都做完整处理,比如重新计算布局、发起网络请求、遍历大数组,主线程就会被占满,渲染和交互都受影响。用户感觉就是卡。
但很多处理其实不需要每次触发都做。搜索联想,用户还在连续打字,中间状态没必要请求;滚动加载,滚动过程中不需要每毫秒都判断;窗口缩放,等用户停下再调整布局更合理。
所以需要一种机制:把高频触发合并成低频执行。防抖和节流就是干这个的。
二、防抖:等停下来再执行
防抖的思路是:事件触发后不立刻执行,而是设定一个等待时间。如果在这个时间内事件又触发了,就重新计时。只有当事件停止触发、等待时间走完,才真正执行一次。
可以想象成电梯门:有人进出就重新计时,等没人进出了,门才关上。防抖就是"等安静下来再行动"。
适用场景:搜索框输入联想、表单实时校验、窗口大小调整后的布局重算、按钮防止重复点击。这些场景的共同点是:中间过程不重要,只关心最终结果。
注意点:如果事件一直触发不停,防抖会导致永远不执行。所以有些场景需要"立即执行一次,之后等停止再执行"的变体。
三、节流:固定频率执行
节流的思路是:不管事件触发多频繁,固定时间内最多执行一次。
可以想象成水龙头滴水:你开得再大,它也是按固定节奏一滴一滴往下掉。节流就是"按节奏来,不跟着事件疯跑"。
适用场景:滚动加载更多、拖拽时更新位置、鼠标移动时的提示、游戏里的按键响应。这些场景的共同点是:需要持续响应,但不能太频繁,固定节奏就够了。
注意点:节流有"首次执行"和"末次执行"的区别。有的实现第一次触发立刻执行,之后按间隔来;有的等间隔到了才执行第一次。选择哪种取决于交互期望。
四、两者的核心区别
防抖是"等停止":事件连续触发时,只在最后执行一次。执行时机取决于事件什么时候停。
节流是"按频率":事件连续触发时,按固定间隔执行多次。执行时机取决于时间间隔,不取决于事件是否停止。
打个比方:防抖像"等所有人都上车了再发车";节流像"每隔十分钟发一班车,不管人多人少"。
选择哪种,取决于你是关心"最终状态"还是"过程中的持续反馈"。
五、常见误区
误区一:防抖节流混用。搜索联想用节流,会导致用户还没打完就发请求,浪费流量;滚动加载用防抖,会导致滚动过程中一直不加载,停下来才加载,体验不好。场景要匹配。
误区二:忘记取消。组件销毁或页面切换时,防抖节流的定时器还在跑,可能触发已经不需要的逻辑,甚至报错。要在适当时机取消。
误区三:等待时间拍脑袋定。时间太短起不到控制频率的作用,太长会让用户觉得反应迟钝。要根据交互特点调整,通常输入类偏短,滚动类偏中等。
误区四:在防抖节流里做重活。它们只控制频率,不减少单次执行的开销。如果单次逻辑本身很重,即使降低频率也可能卡。重活还是要拆分或放到后台线程。
六、实际开发中的组合使用
实际项目里,防抖和节流经常组合使用。
比如搜索联想:用户输入时用防抖,避免每敲一个字就请求;请求返回后渲染结果时,如果结果很多,可以用节流控制渲染频率。
再比如滚动加载:滚动事件用节流控制检查频率,检查到接近底部时,再用防抖确保不会连续触发多次加载。
理解两者的特性,才能灵活组合,而不是死记"输入用防抖、滚动用节流"。
七、总结
防抖和节流解决的是同一个问题:高频事件太多,需要控制执行频率。防抖等停止后执行一次,节流按固定间隔执行多次。选择取决于你是关心最终结果还是持续反馈。理解原理、匹配场景、注意取消和耗时,你就能让页面在高频交互下依然流畅。这是前端性能优化中最实用的一课。