# 前端按钮做防重复点击(防抖与节流)


const debouncedGuideQuestionClick = _.debounce(
    (q: Question) => {
        // 方法内容
    },
    500,
    {
      leading: true,  // 默认false  首调用
      trailing: false, //默认为true  尾调用
    },
  );

// 使用的地方   当点击时 `debouncedGuideQuestionClick` 随后就被调用。
 <div
    className={cls.guideQuestion}
    onClick={() => debouncedGuideQuestionClick(q)}
  >
    {q.question}
  </div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

image.png

# 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秒后强制执行
});

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 毫秒执行
主要用途 只在事件停止后执行 确保周期性执行,避免长时间不执行

# 实际应用场景

  1. 仅用 **wait**:搜索建议,只在用户停止输入300ms后发送请求
const search = _.debounce(fetchResults, 300);

1
2
  1. **wait** + **maxWait**:窗口resize事件,既不想太频繁执行,又不想完全不响应
const handleResize = _.debounce(updateLayout, 100, { maxWait: 500 });
// 用户持续调整窗口时,最多每500ms一定会执行一次

1
2
3
  1. **leading** + **maxWait**:按钮点击防抖,立即响应但限制最高频率
const handleClick = _.debounce(submitForm, 1000, {
  leading: true,
  maxWait: 2000
});
// 第一次点击立即执行,之后1秒内点击不会执行,但如果持续点击,最多每2秒执行一次

1
2
3
4
5
6

maxWait 特别适合那些需要"既要防抖又要确保最终执行"的场景,避免了纯防抖可能导致长期不执行的问题。

# maxWait 与节流(throttle)的关系

你提出了一个很好的观察!当 maxWait 和 wait 一起使用时,确实可以实现类似节流(throttle)的效果,但它们之间有一些关键区别。

# 相似之处

  1. 周期性执行maxWait 确保函数至少每隔 maxWait 毫秒执行一次,这与节流的"固定时间间隔执行"相似

  2. 频率限制:两者都能防止函数过于频繁地执行

# 关键区别

特性 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
});

1
2
3
4
5
6
7
8
9
10

# 何时选择哪种方式

  1. 使用 **throttle**

    • 你明确需要固定间隔执行

    • 代码可读性更重要(明确表达节流意图)

  2. 使用 **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}));

1
2
3
4
5
6
7
8
9

所以回答你的问题: **maxWait** **debounce** 确实可以实现类似节流的效果,但不是完全等同于节流,它们有细微但重要的行为差异。

坑点: 在组件中, 一定要保持防抖函数的引用, 不能因为外面组件的props 变化, 导致里面的防抖函数不断地重新生成, 那样会导致防抖函数不生效, 需要用useCallback 包裹一下防抖函数

最后更新时间: 7/2/2025, 2:56:43 PM