使用 customRef 封装的 debounceRef 可让 v-model 自动防抖,核心是 get 时调用 track()、set 时 clearTimeout 并 setTimeout 后 trigger();支持 cancel 清除定时器、flush 立即更新,并需 onBeforeUnmount 清理。

直接用 customRef 封装一个防抖 ref,就能让 v-model 自动具备防抖能力,不需要额外监听输入事件或写 watch。
核心要点:track 和 trigger 必须成对出现
customRef 不像普通 ref 那样自动处理依赖追踪。它把控制权完全交给你:
- 每次
get时必须调用track()——告诉 Vue “这个值正在被读取”,否则模板首次渲染后就不再响应变化 - 每次真正更新值后必须调用
trigger()——告诉 Vue “值已稳定,该刷新视图了”,否则即使内部变量变了,界面也不更新 - 定时器变量(如
timer)要定义在闭包里,确保每个 ref 实例互不干扰
基础防抖 ref 工具函数
下面是一个可直接复用的 debounceRef 实现:
<!-- debounceRef.js -->
import { customRef } from 'vue'
export function debounceRef(value, delay = 300) {
let timer = null
return customRef((track, trigger) => ({
get() {
track()
return value
},
set(newValue) {
clearTimeout(timer)
timer = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}))
}
在组件中使用(支持 v-model)
就像普通 ref 一样声明和绑定:
const searchQuery = debounceRef('', 500)
<!-- 模板中直接绑定 -->
<input v-model="searchQuery" placeholder="输入搜索关键词..." />
<div>当前值:{{ searchQuery }}</div>
输入过程中,searchQuery 的值不会实时更新;只有用户停顿 500ms 后,值才真正赋值并触发视图更新和依赖响应(比如 computed 重新计算、watch 触发等)。
增强版:支持 cancel 和 flush
实际项目中常需手动干预防抖行为,例如清空搜索或立即提交:
-
cancel():清除待执行的延迟更新,恢复为上一次生效的值 -
flush():立即执行最后一次输入对应的更新,跳过剩余延迟
实现方式是在返回对象中暴露这两个方法,并在内部维护对 timer 和 value 的访问权限。组件卸载时也建议清理定时器,避免内存泄漏(可通过 onBeforeUnmount 调用 cancel)。


















