Skip to content

debounce 防抖函数

在高频触发场景(输入、滚动、窗口尺寸变化等)中,防抖可将多次触发合并为最后一次触发后的执行,有效减少不必要的计算或请求。

安装与引入

按需导入(推荐)

ts
import { debounce } from '@cuixingjian/cui-utils'

子路径导入

ts
import { debounce } from '@cuixingjian/cui-utils/debounce'

效果演示

基础用法

输入内容后,等待 300ms 才会触发搜索,频繁输入只会执行最后一次。

使用代码

基础防抖:

ts
import { debounce } from '@cuixingjian/cui-utils'

const onSearch = (keyword: string) => {
  console.log('搜索:', keyword)
}

const onSearchDebounced = debounce(onSearch, 300)

立即执行模式:

ts
import { debounce } from '@cuixingjian/cui-utils'

const save = (data: any) => {
  console.log('保存数据:', data)
}

const saveDebounced = debounce(save, 500, { immediate: true })

取消与立即触发:

ts
import { debounce } from '@cuixingjian/cui-utils'

const handler = debounce(() => {
  console.log('执行处理')
}, 300)

// 取消等待中的调用
handler.cancel()

// 立即执行最后一次的调用(如果存在)
handler.flush()

API 说明

函数签名

ts
function debounce<T extends (...args: any[]) => any>(
  fn: T,
  wait?: number,
  options?: DebounceOptions
): DebouncedFn<T>

参数

参数名类型默认值说明
fn(...args: any[]) => any需要防抖的函数
waitnumber200等待时间(毫秒)
optionsDebounceOptions{}配置选项

DebounceOptions

参数类型默认值说明
immediatebooleanfalse是否在首次触发时立即执行

返回值

返回一个防抖函数 DebouncedFn<T>,具有以下方法:

方法类型说明
cancel() => void取消等待中的调用
flush() => void立即执行最后一次的调用(如果存在)

注意事项

基本规则

  • 在组件卸载前如仍有防抖等待中的调用,建议调用 cancel() 进行清理
  • 防抖函数会保留 this 上下文和参数

immediate 模式说明

  • immediate: true 时,首次触发会立即执行
  • 在等待窗口内的后续触发被合并
  • 等待窗口关闭后不再触发尾调用

典型场景

  • 搜索框输入:用户停止输入后再发起请求
  • 窗口尺寸变化:调整完成后再重新计算布局
  • 表单验证:输入停止后再进行验证
  • 自动保存:编辑停止后自动保存草稿

源码

展开查看
ts
export interface DebounceOptions {
  immediate?: boolean
}

export interface DebouncedFn<T extends (...args: any[]) => any> {
  (...args: Parameters<T>): void
  cancel: () => void
  flush: () => void
}

export function debounce<T extends (...args: any[]) => any>(
  fn: T,
  wait = 200,
  options: DebounceOptions = {}
): DebouncedFn<T> {
  let timer: ReturnType<typeof setTimeout> | null = null
  let lastArgs: Parameters<T> | null = null
  let lastThis: any = null
  const { immediate = false } = options

  const invoke = () => {
    if (lastArgs) {
      fn.apply(lastThis, lastArgs)
      lastArgs = null
      lastThis = null
    }
  }

  const debounced = function (this: any, ...args: Parameters<T>) {
    lastArgs = args
    lastThis = this

    if (timer) {
      clearTimeout(timer)
      timer = null
    }

    if (immediate && !timer) {
      fn.apply(this, args)
    }

    timer = setTimeout(() => {
      if (!immediate) {
        invoke()
      }
      timer = null
    }, wait)
  } as DebouncedFn<T>

  debounced.cancel = () => {
    if (timer) {
      clearTimeout(timer)
      timer = null
    }
    lastArgs = null
    lastThis = null
  }

  debounced.flush = () => {
    if (timer) {
      clearTimeout(timer)
      timer = null
    }
    invoke()
  }

  return debounced
}