可以使用 customref 封装带防抖的响应式引用:在 set 中清除旧定时器并延后更新 innervalue 和触发 trigger,get 中同步返回当前生效值并调用 track;需手动管理依赖收集与更新通知,卸载时应清理定时器。

可以使用 customRef 封装一个带防抖的响应式引用,核心是控制 get 和 set 的触发时机,把赋值延迟执行,并在延迟期间重复赋值时取消前一次。
理解 customRef 的基本结构
customRef 接收一个工厂函数,返回包含 get 和 set 方法的对象。Vue 依赖收集和触发更新都通过这两个方法控制:
-
get被读取响应式值时调用,需返回当前值(并进行依赖收集) -
set被赋值时调用,接收新值,可在此做拦截、转换或延迟 - 必须手动调用
track()(读取时)和trigger()(变更时),否则响应式失效
实现带防抖的 ref 工厂函数
防抖逻辑放在 set 中:每次设置新值时,清除上一次定时器,重新设定延迟更新。注意要保留原始值用于 get 返回,并确保只有最终生效的值才触发更新:
import { customRef, nextTick } from 'vue'
function useDebouncedRef(value, delay = 200) {
let innerValue = value
let timeout = null
return customRef((track, trigger) => ({
get() {
track() // 告诉 Vue 这里依赖了该 ref
return innerValue
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
innerValue = newValue
trigger() // 值真正更新后才通知依赖更新
}, delay)
}
}))
}
使用示例与注意事项
在组件中直接使用,行为类似普通 ref,但赋值会防抖:
export default {
setup() {
const searchQuery = useDebouncedRef('', 300)
const handleInput = (e) => {
searchQuery.value = e.target.value // 不会立即更新,等待 300ms 静默期
}
return { searchQuery, handleInput }
}
}
- 防抖只影响
set,get仍同步返回最新已提交的值(不是输入中的临时值) - 组件卸载前记得清理定时器(可结合
onBeforeUnmount或watchEffect的清理函数) - 如需支持立即执行首次调用(leading edge),需额外扩展逻辑,标准防抖默认是尾部执行(trailing)
进阶:支持手动取消和立即刷新
可返回增强对象,暴露 flush(立即提交当前值)和 cancel(清空待执行)方法:
function useDebouncedRef(value, delay = 200) {
let innerValue = value
let timeout = null
const flush = () => {
if (timeout) {
clearTimeout(timeout)
timeout = null
// 此时 innerValue 还是旧值,但我们可以选择是否更新
// 若希望 flush 时也更新为最新 pending 值,需额外保存 pending 值
}
}
const cancel = () => clearTimeout(timeout)
return {
...customRef((track, trigger) => ({
get() {
track()
return innerValue
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
innerValue = newValue
trigger()
}, delay)
}
})),
flush,
cancel
}
}











