Vue3 Ref全家桶深度解析:掌握响应式编程精髓
一、Ref核心概念
1.1 响应式数据容器
const count = ref(0)
// 相当于创建了一个响应式容器:
{
value: 0,
__v_isRef: true,
// 其他响应式系统属性
}
1.2 全家桶全景图
二、基础Ref体系
2.1 ref - 深响应式
// 创建响应式引用
const user = ref({
name: 'Alice',
profile: {
age: 25,
hobbies: ['reading', 'coding']
}
})
// 深层修改自动触发更新
user.value.profile.hobbies.push('gaming')
2.2 shallowRef - 浅层响应
const bigObject = shallowRef({
layer1: {
layer2: {
data: '原始值'
}
}
})
// 直接修改.value触发响应
bigObject.value = { ... } ✅
// 深层修改不会触发
bigObject.value.layer1.layer2.data = '新值' ❌
实战场景:
// 处理第三方库实例
const mapInstance = shallowRef(null)
onMounted(() => {
mapInstance.value = new ThirdPartyMap() // 初始化后触发响应
})
三、高级Ref定制
3.1 customRef - 自定义响应逻辑
function useStorageRef(key, defaultValue) {
return customRef((track, trigger) => ({
get() {
track()
const value = localStorage.getItem(key)
return value ? JSON.parse(value) : defaultValue
},
set(newValue) {
localStorage.setItem(key, JSON.stringify(newValue))
trigger()
}
}))
}
// 使用示例
const userSettings = useStorageRef('settings', { theme: 'light' })
3.2 防抖搜索实战
function useDebouncedRef(initialValue, delay = 300) {
let timeout
return customRef((track, trigger) => ({
get() {
track()
return initialValue
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
initialValue = newValue
trigger()
}, delay)
}
}))
}
四、Ref转换体系
4.1 toRef - 保持响应连接
const state = reactive({ count: 0 })
const countRef = toRef(state, 'count')
countRef.value++ // state.count 同步变为1
4.2 toRefs - 解构响应对象
<script setup>
function usePosition() {
const pos = reactive({ x: 0, y: 0 })
return {
...toRefs(pos),
reset: () => { pos.x = pos.y = 0 }
}
}
const { x, y } = usePosition()
</script>
<template>
<div>X: {{ x }}, Y: {{ y }}</div>
</template>
4.3 响应式Props处理
const props = defineProps(['user'])
const userRef = toRef(props, 'user')
watch(userRef, (newVal) => {
console.log('用户信息更新:', newVal)
})
五、Ref工具函数
5.1 isRef - 类型检查
const maybeRef = ref(0)
console.log(isRef(maybeRef)) // true
console.log(isRef({ value: 0 })) // false
// 类型守卫用法
function handleValue(input) {
if (isRef(input)) {
console.log(input.value)
} else {
console.log(input)
}
}
5.2 unref - 智能取值
const a = ref(10)
const b = 20
console.log(unref(a)) // 10
console.log(unref(b)) // 20
// 在组合式函数中的妙用
function useSmartLogger(value) {
const val = unref(value)
console.log('当前值:', val)
}
// 可以接受ref或普通值
useSmartLogger(a) // 10
useSmartLogger(b) // 20
六、全家桶对比指南
API | 核心功能 | 典型应用场景 | 注意事项 |
---|---|---|---|
ref | 创建深响应式引用 | 基本类型/对象/数组 | 需要.value访问 |
shallowRef | 创建浅层响应式引用 | 大对象/类实例/第三方库实例 | 深层修改需手动触发 |
customRef | 自定义响应式容器 | 防抖/节流/本地存储集成 | 需要手动track/trigger |
toRef | 保持源响应式连接 | 从reactive对象提取属性 | 源属性必须存在 |
toRefs | 解构响应式对象 | 组合式函数返回值/Props解构 | 保持响应式连接 |
isRef | 检测Ref对象 | 类型检查/条件处理 | 不能检测reactive对象 |
unref | 安全获取值 | 处理可能为Ref的值 | 等价于val = isRef(v) ? v.value : v |
七、实战最佳实践
7.1 Ref与Reactive的黄金组合
const state = reactive({
count: ref(0), // 基本类型用ref包装
user: { // 对象直接嵌套
name: 'Alice',
age: ref(25) // 混合使用
}
})
7.2 组合式函数参数处理
// 同时接受ref和普通值
function useSmartAdd(target) {
return computed(() => unref(target) + 10)
}
const num = ref(5)
console.log(useSmartAdd(num).value) // 15
console.log(useSmartAdd(20).value) // 30
7.3 类型安全进阶
interface User {
name: string
age: number
}
// Ref类型推断
const user = ref<User>({
name: 'Bob',
age: 30
})
// 自定义Ref类型
function useTimestampRef(): Ref<number> {
return ref(Date.now())
}
八、常见陷阱破解
Q:为什么修改shallowRef的深层属性不触发更新?
A:shallowRef只监听.value的替换,需要强制更新时:
import { triggerRef } from 'vue'
shallowRef.value.deepProp = 'new'
triggerRef(shallowRef) // 手动触发
Q:toRefs解构后如何保持类型提示?
const state = reactive({ x: 0, y: 0 })
const { x, y } = toRefs(state) // 自动推断为Ref<number>
Q:unref在组合式函数中的典型应用?
// 参数智能处理
function usePosition(target) {
const x = ref(unref(target.x))
const y = ref(unref(target.y))
// ...
}
九、生态整合
9.1 与Vue Router整合
import { useRoute } from 'vue-router'
const route = useRoute()
watch(() => route.params.id, (newId) => {
// 处理路由变化
})
9.2 状态管理集成
// Pinia示例
import { useStore } from './store'
const store = useStore()
const doubleCount = computed(() => store.count * 2)
十、总结升华
Ref全家桶是Vue3响应式系统的瑞士军刀:
- 核心三剑客:ref/shallowRef/customRef 构建响应式基础
- 转换双雄:toRef/toRefs 打通响应式血脉
- 工具搭档:isRef/unref 提升开发体验
掌握这些工具的组合使用,就像拥有了响应式编程的乐高积木,可以搭建出各种复杂的响应式系统。记住三个黄金原则:
- 按需选择:根据数据结构选择响应深度
- 类型安全:充分利用TS类型系统
- 组合优先:通过组合简单Ref构建复杂逻辑
(本文代码示例基于Vue3.4+,部分特性需注意浏览器兼容性)