# 前端按钮做防重复点击(防抖与节流)
const debouncedGuideQuestionClick = _.debounce(
(q: Question) => {
// 方法内容
},
500,
{
leading: true, // 默认false 首调用
trailing: false, //默认为true 尾调用
},
);
// 使用的地方 当点击时 `debouncedGuideQuestionClick` 随后就被调用。
<div
className={cls.guideQuestion}
onClick={() => debouncedGuideQuestionClick(q)}
>
{q.question}
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Lodash 中 debounce 函数的参数解释
_.debounce(func, [wait=0], [options={}]) 是 Lodash 中的一个实用函数,用于限制函数的执行频率,常用于处理高频触发的事件(如窗口大小调整、滚动、输入等)。
# 参数详解
# 1. func (Function)
必需参数
要防抖的函数,即需要延迟执行的函数
# 2. [wait=0] (number)
可选参数,默认值为 0
延迟的毫秒数,表示函数在执行前需要等待的时间
在这段时间内如果再次触发,则重新计时
# 3. [options={}] (Object)
可选参数,默认值为空对象
配置对象,可以包含以下属性:
# options.leading (boolean)
是否在延迟开始前立即调用函数
默认值:
false如果设为
true,则在第一次触发时立即执行函数,然后进入防抖模式
# options.trailing (boolean)
是否在延迟结束后调用函数
默认值:
true如果设为
false,则仅当leading为true时可能执行函数
# options.maxWait (number)
设置函数被延迟执行的最大等待时间
即使不断触发,函数也会在
maxWait时间后强制执行
# 使用示例
// 基本用法
const debouncedFn = _.debounce(() => {
console.log('执行防抖函数');
}, 300);
// 带选项的用法
const debouncedFnWithOptions = _.debounce(() => {
console.log('执行带选项的防抖函数');
}, 300, {
leading: true, // 第一次立即执行
trailing: false // 延迟结束后不执行
});
// 带maxWait的用法
const debouncedFnWithMaxWait = _.debounce(() => {
console.log('执行带maxWait的防抖函数');
}, 300, {
maxWait: 1000 // 最多等待1秒后强制执行
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 注意事项
leading和trailing不能同时为false,否则函数永远不会执行如果
wait为 0 且leading为false,则函数会在下一个事件循环中执行返回的是一个防抖后的新函数,原始函数不会被修改
# maxWait 和 wait 参数的区别
在 Lodash 的 debounce 函数中,wait 和 maxWait 都是控制函数执行时间的参数,但它们的作用不同:
# wait 参数
作用:设置防抖的等待时间(毫秒)
行为:
每次触发函数时都会重置计时器
只有在最后一次触发后等待
wait毫秒没有新触发时,才会执行函数
效果:确保函数只在"停止触发"后
wait毫秒执行一次
# maxWait 参数
作用:设置函数被延迟执行的最大等待时间(毫秒)
行为:
从第一次触发开始计时
即使持续触发,函数也会在
maxWait时间后强制执行之后会重置
maxWait计时器
效果:确保函数至少每隔
maxWait毫秒执行一次
# 关键区别
| 特性 | wait | maxWait |
|---|---|---|
| 计时起点 | 每次触发都重置计时 | 第一次触发开始计时,不因后续触发重置 |
| 执行条件 | 停止触发后等待 wait 毫秒执行 | 从第一次触发开始最多等待 maxWait 毫秒执行 |
| 主要用途 | 只在事件停止后执行 | 确保周期性执行,避免长时间不执行 |
# 实际应用场景
- 仅用
**wait**:搜索建议,只在用户停止输入300ms后发送请求
const search = _.debounce(fetchResults, 300);
2
**wait**+**maxWait**:窗口resize事件,既不想太频繁执行,又不想完全不响应
const handleResize = _.debounce(updateLayout, 100, { maxWait: 500 });
// 用户持续调整窗口时,最多每500ms一定会执行一次
2
3
**leading**+**maxWait**:按钮点击防抖,立即响应但限制最高频率
const handleClick = _.debounce(submitForm, 1000, {
leading: true,
maxWait: 2000
});
// 第一次点击立即执行,之后1秒内点击不会执行,但如果持续点击,最多每2秒执行一次
2
3
4
5
6
maxWait 特别适合那些需要"既要防抖又要确保最终执行"的场景,避免了纯防抖可能导致长期不执行的问题。
# maxWait 与节流(throttle)的关系
你提出了一个很好的观察!当 maxWait 和 wait 一起使用时,确实可以实现类似节流(throttle)的效果,但它们之间有一些关键区别。
# 相似之处
周期性执行:
maxWait确保函数至少每隔maxWait毫秒执行一次,这与节流的"固定时间间隔执行"相似频率限制:两者都能防止函数过于频繁地执行
# 关键区别
| 特性 | debounce 带 maxWait | throttle |
|---|---|---|
| 触发机制 | 需要持续触发才会周期性执行 | 无论是否持续触发都会周期性执行 |
| 首次执行 | 取决于 leading 选项 | 取决于 leading 选项 |
| 末次执行 | 取决于 trailing 选项 | 总是会执行最后一次调用 |
| 内部实现 | 是防抖功能的扩展 | 专门设计的节流机制 |
# 实际等价关系
在 Lodash 中,以下两种写法几乎是等价的:
// 使用 throttle
const throttledFn = _.throttle(func, wait, {leading, trailing});
// 使用 debounce 带 maxWait
const debouncedFn = _.debounce(func, wait, {
leading,
trailing,
maxWait: wait
});
2
3
4
5
6
7
8
9
10
# 何时选择哪种方式
使用
**throttle**当:你明确需要固定间隔执行
代码可读性更重要(明确表达节流意图)
使用
**debounce**带**maxWait**当:你需要更精细的控制(如不同的
wait和maxWait值)你需要结合防抖和节流的混合行为
你需要只在持续触发时才周期性执行
# 示例说明
// 纯防抖 - 只在停止滚动后执行
window.addEventListener('scroll', _.debounce(update, 250));
// 节流 - 每250ms最多执行一次
window.addEventListener('scroll', _.throttle(update, 250));
// 防抖带maxWait - 滚动时至少每500ms执行一次,停止后250ms再执行一次
window.addEventListener('scroll', _.debounce(update, 250, {maxWait: 500}));
2
3
4
5
6
7
8
9
所以回答你的问题:带 **maxWait** 的 **debounce** 确实可以实现类似节流的效果,但不是完全等同于节流,它们有细微但重要的行为差异。
坑点: 在组件中, 一定要保持防抖函数的引用, 不能因为外面组件的props 变化, 导致里面的防抖函数不断地重新生成, 那样会导致防抖函数不生效, 需要用useCallback 包裹一下防抖函数