一句话:节流是「隔一段时间才准执行一次」,防抖是「停下来才执行」——滚动用节流,搜索输入用防抖。
写在前面
高频事件(scroll、resize、input)若每次触发都请求接口或重算布局,会拖垮性能。 节流(throttle) 和 防抖(debounce) 是前端最常用的两种包装策略。二者区别常被记混:核心在于 首次是否立即执行 以及 连续触发时的计时逻辑。
核心内容
节流(throttle)
语义:在固定时间间隔内,无论触发多少次,函数最多执行一次。
典型场景:页面滚动加载、鼠标移动跟踪、按钮防重复点击、拖拽。
时间戳版(首次立即执行,结束边界可能多触发一次):
javascript
export function throttle(fn, delay) {
let lastTime = 0
return function (...args) {
const now = Date.now()
if (now - lastTime >= delay) {
fn.apply(this, args)
lastTime = now
}
}
}
定时器版(首次也受 delay 约束,结束更均匀):
javascript
export function throttle(fn, delay = 500) {
let flag = true
return function (...args) {
if (flag) {
fn.apply(this, args)
flag = false
setTimeout(() => { flag = true }, delay)
}
}
}
使用示例:
javascript
const throttledScroll = throttle(() => {
console.log('滚动中...')
}, 2000)
window.addEventListener('scroll', throttledScroll)
防抖(debounce)
语义:连续触发时只认「最后一次」;每次触发都重置等待计时,静默 delay 毫秒后才真正执行。
典型场景:搜索框联想、窗口 resize 结束后重绘、表单校验。
javascript
export function debounce(fn, delay) {
let timerId = null
return function (...args) {
if (timerId) clearTimeout(timerId)
timerId = setTimeout(() => {
fn.apply(this, args)
timerId = null
}, delay)
}
}
javascript
const debouncedInput = debounce(() => {
console.log('输入稳定,发起搜索')
}, 500)
document.getElementById('inputField').addEventListener('input', debouncedInput)
leading 防抖(可选):第一次立即执行,后续连续输入仍合并——lodash 的 debounce(fn, wait, { leading: true, trailing: false }) 即此类。
对比一览
| 维度 | 节流 | 防抖 |
|---|---|---|
| 连续触发 | 按固定间隔执行 | 重新计时,静默后才执行 |
| 首次触发 | 时间戳版立即执行 | 默认延迟后执行 |
| 比喻 | 地铁发车间隔 | 电梯等人齐再关门 |
| 场景 | scroll、mousemove | input 搜索、resize |
sequenceDiagram
participant E as 事件
participant T as 节流
participant D as 防抖
Note over E,T: 每 200ms 最多 1 次
E->>T: 触发×10
T->>T: 执行约 5 次
Note over E,D: 停止输入 500ms 后 1 次
E->>D: 触发×10
D->>D: 最终执行 1 次
Vue / 现代工具库
- VueUse:
useDebounceFn、useThrottleFn - lodash:
debounce、throttle(注意throttle已标记 deprecated,新项目可自实现或换@vueuse/core)
在线对比 Demo:jiang-xia.top/tool/test
踩坑
- 箭头函数丢失 this:包装函数内用
fn.apply(this, args)保留调用上下文。 - 组件卸载未取消:debounce 的 timer 应在
onUnmounted里clearTimeout,或取消防抖函数的 pending 调用。 - 节流 trailing 丢失最后一次:时间戳版可能在最后一次事件后不再执行;可结合 trailing 标志或使用 lodash 选项。
- delay 单位混用:统一毫秒;与
requestAnimationFrame配合时 scroll 动画更顺滑。 - 把 debounce 当节流用:搜索框用 throttle 会导致每 300ms 打一次接口,仍过多。
小结
- 节流:控频率,滚动/拖拽/按钮连点。
- 防抖:等稳定,搜索/resize/提交校验。
- 实现要点:闭包保存 timer / lastTime;记得
apply(this, args)与卸载清理。

全部评论(0)