customRef 是 Vue 3 中提供的一个 响应式 API,允许你自定义 ref 的行为 —— 它是响应式系统的底层构建砖块之一。如果你已经玩转了 ref、reactive,但觉得有时候想插个拦截器、做点节流防抖之类的骚操作,那么 customRef 就是你打开新世界的钥匙。
🌱 基础介绍
customRef允许我们手动控制 getter 和 setter 的行为,并且通过track和trigger精确控制依赖收集与更新触发的时机。
track():在get()中调用,用于收集依赖, 告诉 Vue “我这里有人要监听我”, 如果不调用track(),这个值更新时,Vue 不会重新渲染组件,watch()也不会触发trigger():在set()中调用,用于触发依赖, 告诉 Vue “我变了,请通知所有监听我的人”
语法
customRef<T>(factory: (track: () => void, trigger: () => void) => {
get: () => T
set: (value: T) => void
}): Ref<T>
🔧 场景一:防抖输入(最经典用法)
import { customRef } from 'vue'
function useDebouncedRef(value, delay = 300) {
let timeout
return customRef((track, trigger) => ({
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}))
}
使用方式:
const keyword = useDebouncedRef('', 500)
watch(keyword, val => {
console.log('debounced:', val)
})
🎯 说明:只有在用户停止输入 500ms 后,watch 才会触发。
🔍 场景二:只读 ref(拦截 set)
function useReadonlyRef(value) {
return customRef((track) => ({
get() {
track()
return value
},
set() {
console.warn('This ref is readonly.')
}
}))
}
const readOnlyValue = useReadonlyRef('Vue3')
readOnlyValue.value = 'Try to change me' // 控制台输出警告
🕵️ 场景三:带缓存机制的 ref(懒加载)
function useLazyRef(factory) {
let loaded = false
let val
return customRef((track, trigger) => ({
get() {
track()
if (!loaded) {
val = factory()
loaded = true
trigger()
}
return val
},
set(newVal) {
val = newVal
trigger()
}
}))
}
const data = useLazyRef(() => {
console.log('init only once')
return Math.random()
})
✅ 初次访问才会触发 factory,后面就是缓存值。
🎛️ 场景四:双向绑定 + 中间状态控制(输入同步但延迟提交)
function useDelayedSubmitRef(initValue, delay = 1000) {
let timeout
let internalValue = initValue
return customRef((track, trigger) => ({
get() {
track()
return internalValue
},
set(value) {
internalValue = value
trigger() // 立即更新 UI
clearTimeout(timeout)
timeout = setTimeout(() => {
// 模拟提交逻辑
console.log('Submitted:', value)
}, delay)
}
}))
}
🧠 总结一句话
customRef 就像是 Vue 响应式系统里开了个“钩子口”,你可以在 get 和 set 之间 做任何你想做的事 —— 防抖、节流、只读、懒加载、数据拦截、中间态……只要你敢想,它就敢干
📦 建议用法
- 💬 用户输入防抖(推荐)
- 📦 表单缓存
- 🧲 数据懒加载
- 🔒 只读 ref 封装
- 🧩 封装业务型组件时对外暴露特殊响应式值