Vue3 Ref全家桶详解:从入门到实战
一、为什么需要Ref?
在Vue3的响应式系统中,ref
就像给你的数据套上一个智能包装盒。想象你有一瓶普通矿泉水(基本类型数据),直接拿着瓶子摇晃(修改)别人是感知不到的。但如果你把它装进智能水杯(ref)里摇晃,杯子就会自动亮灯提醒大家"水被摇过啦!"
基本类型响应式困境
let count = 0
function increment() {count++ // 修改了但视图不会更新!
}
二、基础Ref用法
2.1 创建响应式数据
import { ref } from 'vue'const count = ref(0) // 套上智能包装盒
const user = ref({ name: '张三' }) // 对象也能装!
2.2 访问与修改
console.log(count.value) // 0 → 需要.value访问
count.value++ // 视图自动更新!// 对象操作
user.value.name = '李四' // 自动触发更新
2.3 模板中使用
<template><!-- 自动解包.value --><div>{{ count }}</div><button @click="count++">+1</button>
</template>
三、ShallowRef:浅层响应式
当你的对象像俄罗斯套娃时:
const nestedObj = shallowRef({layer1: {layer2: {value: '深层数据'}}
})// 触发响应:
nestedObj.value = { ... } // 不触发响应:
nestedObj.value.layer1.layer2.value = '新值'
适用场景:
- 大型不可变对象
- 第三方库实例
- 需要手动控制更新的场景
四、CustomRef:自定义Ref
自己造一个带震动提醒的智能水杯:
function useDebouncedRef(value, delay = 200) {let timeoutreturn customRef((track, trigger) => ({get() {track() // 标记依赖return value},set(newValue) {clearTimeout(timeout)timeout = setTimeout(() => {value = newValuetrigger() // 触发更新}, delay)}}))
}// 使用示例
const searchText = useDebouncedRef('', 500)
五、ToRef家族
5.1 toRef:保持响应连接
const state = reactive({ foo: 1 })
const fooRef = toRef(state, 'foo') // ↔ 保持连接fooRef.value++
console.log(state.foo) // 2
5.2 toRefs:解构不丢失响应
// 在组合式函数中
function useFeature() {const state = reactive({ x: 0, y: 0 })return { ...toRefs(state) } // 拆分成独立ref
}// 在组件中使用
const { x, y } = useFeature() // 依然保持响应!
5.3 在Props中的妙用
<script setup>
const props = defineProps(['user'])
const { user } = toRefs(props) // 安全解构watch(user, (newVal) => {console.log('用户信息更新了!')
})
</script>
六、Ref全家桶对比手册
方法 | 特点 | 典型场景 | 注意事项 |
---|---|---|---|
ref | 深响应式 | 基本类型/需替换的对象 | 记得.value |
shallowRef | 浅层响应 | 大对象/类实例 | 深层修改需forceUpdate |
customRef | 自定义存取逻辑 | 防抖/校验/转换 | 注意内存泄漏 |
toRef | 保持源响应连接 | 从响应式对象提取单个属性 | 源失效则ref失效 |
toRefs | 解构保持响应性 | 组合式函数返回值/Props解构 | 配合reactive使用更佳 |
七、实战技巧
7.1 模板Ref绑定组件
<template><ChildComponent ref="child" />
</template><script setup>
import { ref } from 'vue'const child = ref(null) // 获得组件实例
</script>
7.2 配合watch使用
const count = ref(0)watch(count, (newVal, oldVal) => {console.log(`从${oldVal}变为${newVal}`)
}, { immediate: true })
7.3 TypeScript支持
interface User {name: stringage: number
}const user = ref<User>({name: 'Alice',age: 25
})
八、常见问题解答
Q:什么时候用ref?什么时候用reactive?
A:简单数据用ref,复杂对象用reactive。当需要保持引用稳定时优先ref。
Q:为什么我的ref修改没触发更新?
检查点:
- 是否正确使用.value
- 是否直接修改了数组的length
- 是否在异步回调中操作(需用shallowRef时)
Q:toRefs会复制对象吗?
不会!它只是创建代理,原对象修改仍会影响所有toRefs创建的ref。
九、总结
Ref全家桶就像瑞士军刀:
ref
是主刀,处理日常需求shallowRef
是剪刀,处理特殊结构customRef
是锉刀,定制特殊需求toRef(s)
是开瓶器,解决解构难题
记住:每个工具都有适用场景,理解原理后灵活组合,才能写出优雅高效的Vue代码!
(注:本文示例基于Vue3.4版本,部分特性需注意版本兼容性)