在 Vue 3 的响应式系统中,shallowRef
、triggerRef
和 customRef()
是用于精细化控制响应性行为的高级 API。它们适用于需要对响应式更新进行细粒度控制的场景。
🧩 一、shallowRef
✅ 作用:
创建一个 仅追踪 .value
引用变化 的 ref,不会对对象内部属性进行深层响应式转换。
示例:
import { shallowRef, watchEffect } from 'vue'
const state = shallowRef({
count: 0,
user: { name: 'Alice' }
})
watchEffect(() => {
console.log('state.value:', state.value)
})
state.value.count++ // 不会触发 watchEffect 更新
state.value = { count: 1 } // 会触发更新
❗只有当整个
.value
被替换时才会触发更新。
🔁 二、triggerRef
✅ 作用:
手动触发一个 shallowRef
的依赖更新。常用于你修改了 shallowRef
内部状态但不想或不能替换整个 .value
时。
示例:
import { shallowRef, triggerRef } from 'vue'
const obj = shallowRef({ count: 0 })
obj.value.count++
triggerRef(obj) // 手动触发更新
⚠️
triggerRef
只能用于shallowRef
或使用customRef
自定义行为的对象。
🛠 三、customRef()
✅ 作用:
自定义一个 ref 的响应式行为,允许你完全控制 track
(依赖收集)和 trigger
(触发更新)逻辑。
使用场景:
- 实现防抖/节流的 ref
- 延迟更新
- 外部数据源同步
- 需要精细控制响应式的复杂场景
示例:实现一个带防抖功能的 ref
useDebouncedRef
函数的返回值是一个 ref
对象,它具有 .value
属性,并且其行为符合 Vue 的响应式系统规则。
import { customRef } from 'vue'
import { debounce } from 'lodash-es'
export function useDebouncedRef(value, delay = 200) {
return customRef((track, trigger) => {
let currentValue = value
const debouncedTrigger = debounce(() => {
trigger() // 触发响应式更新
}, delay)
return {
get() {
track() // 收集依赖
return currentValue
},
set(newValue) {
currentValue = newValue
debouncedTrigger()
}
}
})
}
✅ 返回值说明:
customRef()
是 Vue 提供的一个 API,用于创建一个自定义行为的ref
。- 它返回的是一个 带有
.value
属性的对象。 - 这个对象可以像普通
ref
一样使用:
const searchQuery = useDebouncedRef('');
console.log(searchQuery.value); // 获取值
searchQuery.value = 'hello'; // 设置值
使用方式:
const searchQuery = useDebouncedRef('') <input v-model="searchQuery" />
因为 Vue 模板中的 v-model
会自动解包 ref(即自动读取 .value
并在赋值时调用 .set()
)。
这是 Vue 3 的特性之一:在模板中使用 ref 时,会自动解包 .value
。
同样的逻辑也适用于 v-model
,所以你可以放心地把 ref
对象传给 v-model
。
表达式 | 是否正确 | 说明 |
---|---|---|
useDebouncedRef('') 返回值是 ref 对象 | ✅ 正确 | 例如 { value: '...' } |
<input v-model="searchQuery" /> 绑定该对象 | ✅ 正确 | Vue 模板会自动解包 .value |
v-model 是否要求必须是原始值? | ❌ 否 | 可以是 ref 对象,Vue 会自动处理 |
🎯 此输入框的值会在用户停止输入后 200ms 才触发响应式更新。
📊 对比总结表
API | 特点 | 适用场景 |
---|---|---|
ref | 深层响应式,自动解包嵌套 ref | 默认首选,通用响应式 |
shallowRef | 仅响应 .value 变化,不处理内部结构 | 性能敏感、大对象优化 |
triggerRef | 手动触发 shallowRef 更新 | 修改内部属性后主动刷新 |
customRef() | 完全自定义响应式行为 | 高级定制,如防抖、异步加载 |
💡 最佳实践建议
- 默认使用
ref
,除非你有明确的性能或行为控制需求。 - 性能敏感场景(如大型对象、频繁局部更新)使用
shallowRef
+triggerRef
。 - 需要精确控制响应式更新时机(如搜索框防抖)时使用
customRef()
。 - 避免滥用
triggerRef
,它破坏了 Vue 的自动依赖追踪机制,应谨慎使用。