customRef 实现防抖的核心是接管 ref 的 getter/setter,延迟更新 internalValue 并清除上一定时器;需在 get 中调用 track()、set 中异步触发 trigger(),确保响应式正确。

用 customRef 实现输入框防抖,核心是**接管 ref 的 getter/setter,延迟更新内部值,并在触发时取消上一次未执行的更新**。它比直接用 watch + setTimeout 更精准——不依赖副作用监听,而是从响应式源头控制赋值时机。
理解 customRef 的基本结构
customRef 接收一个工厂函数,返回包含 get 和 set 的对象:
- get:每次访问 ref 值时调用,应返回当前有效值
-
set:每次给 ref 赋值时调用(比如
v-model输入),可在此做防抖逻辑 - 需手动维护一个内部变量(如
value)和定时器引用(如timeout)
编写防抖 ref 工厂函数
以下是一个通用、可复用的防抖 ref 创建函数:
import { customRef } from 'vue'
export function useDebouncedRef(value, delay = 300) {
let internalValue = value
let timeout = null
return customRef((track, trigger) => ({
get() {
track() // 告诉 Vue 这个值被读取了,需要追踪
return internalValue
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
internalValue = newValue
trigger() // 告诉 Vue 值已变更,通知依赖更新
}, delay)
}
}))
}
关键点说明:
-
track()必须在get中调用,否则响应式依赖无法建立 -
trigger()必须在异步更新后调用,否则视图不会刷新 - 每次
set都清除旧定时器,确保只响应最后一次输入
在组件中使用防抖 ref
模板中仍可直接用 v-model,就像普通 ref 一样:
<template><input v-model="debouncedInput" placeholder="输入试试(300ms 防抖)"><p>当前值:{{ debouncedInput }}</p>
</template><script setup>
import { useDebouncedRef } from './useDebouncedRef.js'
const debouncedInput = useDebouncedRef('', 500)
</script>
注意:
- 输入过程中
debouncedInput.value不会实时变化,只有停顿后才更新 - 若需立即获取原始输入(如校验、搜索建议),可额外监听
@input或用ref存原始值 - 组件卸载前记得清理定时器(Vue 3.4+ 自动清理;旧版可加
onBeforeUnmount)
扩展:支持立即执行 + 取消功能
进阶场景下,你可能希望首次输入立刻生效,或手动强制提交:
export function useDebouncedRef(value, delay = 300) {
let internalValue = value
let timeout = null
const commit = () => {
if (timeout) {
clearTimeout(timeout)
timeout = null
// 立即触发更新
internalValue = internalValue // 确保值已设
trigger()
}
}
return customRef((track, trigger) => ({
get() {
track()
return internalValue
},
set(newValue) {
clearTimeout(timeout)
internalValue = newValue // 先存新值
timeout = setTimeout(() => {
trigger()
}, delay)
}
}))
}
这样就能通过 debouncedInput.commit() 主动提交当前值。










