服务端阅读 06月1日 02:06
Lodash 防抖和节流有什么区别?各自适用什么场景?
防抖(debounce)和节流(throttle)都用于限制函数执行频率,但策略不同。防抖在事件停止触发后才执行——每次触发都重置计时器,所以连续触发期间函数不会执行,只在最后一次触发后的延迟时间到达时执行一次。节流则按固定时间间隔执行,不管事件触发多频繁,函数最多按间隔执行。核心区别:防抖关注"最后一次",节流关注"固定频率"。防抖适用于搜索框输入、窗口resize、表单验证——这些场景只关心最终状态。节流适用于滚动事件、鼠标移动、动画帧——这些场景需要持续响应但不能过于频繁。Lodash的_.debounce和_.throttle都支持leading(首次是否立即执行)和trailing(结束是否执行)选项,以及cancel()方法取消待执行的调用。// 防抖:每次触发重置计时器,只在停止后执行function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); };}// 节流:固定间隔执行,期间触发会被忽略或缓存function throttle(func, wait) { let timeout, previous = 0; return function(...args) { const now = Date.now(); if (now - previous > wait) { func.apply(this, args); previous = now; } else { clearTimeout(timeout); timeout = setTimeout(() => { func.apply(this, args); previous = Date.now(); }, wait - (now - previous)); } };}Lodash用法示例:import _ from 'lodash';// 防抖:搜索框300ms停止输入后才发请求const debouncedSearch = _.debounce(keyword => fetchResults(keyword), 300);// 节流:滚动最多每100ms触发一次const throttledScroll = _.throttle(() => checkLoadMore(), 100);window.addEventListener('scroll', throttledScroll);// leading/trailing选项_.debounce(fn, 300, { leading: true, trailing: false }); // 首次立即执行_.throttle(fn, 100, { leading: false, trailing: true }); // 首次不执行// 组件卸载时取消debouncedSearch.cancel();throttledScroll.cancel();图示对比:防抖:触发 ●●●●●●●●●● → 执行 ●节流:触发 ●●●●●●●●●● → 执行 ● ● ●追问leading和trailing选项怎么理解?Lodash的防抖和节流都有leading和trailing两个布尔选项。leading: true表示延迟期开始时立即执行一次,trailing: true表示延迟期结束时再执行一次。默认值:防抖是leading: false, trailing: true(只在停止后执行),节流是leading: true, trailing: true(首尾各执行一次)。常见的配置:防抖搜索用{ leading: false, trailing: true }(默认);节流滚动用{ leading: true, trailing: true }(默认);按钮防重复点击可用{ leading: true, trailing: false }确保只执行首次。防抖和节流在React中有什么坑?React函数组件中每次渲染都会创建新的防抖/节流函数,导致无法正确缓存计时器。需要用useRef或useMemo保持同一个引用:// 错误:每次渲染创建新实例const handleClick = _.debounce(fn, 300); // 无效// 正确:useRef保持引用const debouncedFn = useRef(_.debounce(fn, 300)).current;// 或用useMemoconst debouncedFn = useMemo(() => _.debounce(fn, 300), []);另外,组件卸载时要调用.cancel()清理待执行的定时器,否则可能对已卸载组件执行操作导致报错。防抖的cancel和flush方法是做什么的?cancel()取消待执行的延迟调用,清除计时器。场景:用户在防抖延迟期间主动提交表单,此时应该cancel()掉防抖,直接走提交逻辑。flush()立即执行当前待执行的延迟调用,如果当前没有待执行则什么都不做。场景:用户在防抖等待期间离开页面,可以用flush()确保最后一次输入被处理。两者都可在组件卸载时使用,cancel放弃执行,flush确保执行。防抖能实现节流效果吗?可以。防抖设置{ leading: true, trailing: true }加上maxWait选项就能近似节流效果。maxWait指定函数被延迟执行的最大时间,超过这个时间必定执行一次。_.throttle(fn, wait)实际上等价于_.debounce(fn, wait, { maxWait: wait })。反过来,节流无法实现防抖效果,因为节流无法做到"只在停止后执行"。