customRef 是 Vue 3 实现防抖或节流响应式数据最干净的方式:防抖 ref 通过重置定时器延迟更新值并调用 track()/trigger() 确保模板响应;节流 ref 则按时间间隔限制更新频率;需注意避免漏调 track() 或 trigger() 及共享状态导致的干扰。

Vue 3 的 customRef 是实现防抖或节流响应式数据最干净的方式——它不依赖额外事件监听或手动拆解 v-model,而是直接改造 ref 的读写行为,让模板绑定依然自然可用。
防抖 ref:输入后延迟更新值
核心是每次赋值都重置定时器,只在用户“停顿”后才真正变更值并通知 Vue 更新:
-
get 里必须调用
track(),否则模板无法建立依赖,值变了也不刷新 -
set 里清掉旧定时器,延迟赋值后再调用
trigger(),确保只有最后一次输入生效 - 延迟时间可配置,适合搜索框、筛选条件等场景
示例代码:
function useDebouncedRef(value, delay = 500) {let timeout
return customRef((track, trigger) => ({
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}))
}
组件中直接使用:const search = useDebouncedRef('', 300),然后 <input v-model="search" /> 就具备防抖能力。
立即学习“前端免费学习笔记(深入)”;
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
节流 ref:固定间隔内最多更新一次
适用于滚动位置、鼠标移动等高频触发但只需定期采样的场景。逻辑是记录上一次触发时间,未到间隔就忽略新值:
- 用
Date.now()判断是否满足最小间隔 - 仅当达到间隔才更新内部值并触发响应式更新
- 不会累积延迟,保证节奏稳定
示例代码:
function useThrottledRef(value, delay = 100) {let lastTriggerTime = 0
return customRef((track, trigger) => ({
get() {
track()
return value
},
set(newValue) {
const now = Date.now()
if (now - lastTriggerTime >= delay) {
value = newValue
lastTriggerTime = now
trigger()
}
}
}))
}
例如:const scrollY = useThrottledRef(0, 160)(约每帧一次),配合 @scroll 手动赋值即可控制更新频率。
注意事项:避免常见陷阱
customRef 灵活但容易出错,关键点很明确:
- 漏掉
track()→ 模板不响应变化,值变了也看不到 - 漏掉
trigger()→ 值已更新,但依赖没收到通知,视图卡住 - 在
set中直接改值却不调trigger()→ 表面有效,实际无响应 - 多个
customRef共享同一个变量或定时器 → 相互干扰,行为不可控
每个 customRef 实例应管理自己独立的状态(如自己的 timeout 或 lastTriggerTime)。

















