Vue 3 中 ref 和 reactive 的使用区别与场景
| 特性 |
ref
|
reactive
|
|---|---|---|
| 数据类型 | 基本类型 + 对象/数组 | 仅对象/数组(引用类型) |
| 访问方式 |
.value 访问/修改
|
直接访问属性 |
| 响应式原理 |
通过 value 属性的 getter/setter
|
通过 Proxy 代理整个对象 |
| 解构影响 | 解构会丢失响应式 | 解构也会丢失响应式 |
| 重新赋值 | 整个替换依然响应式 | 重新赋值会丢失响应式 |
ref 的场景
// 1. 基本类型数据(必须用 ref)
const count = ref(0)
const name = ref('Vue')
const isActive = ref(true)
// 2. 需要整体替换的对象/数组
const formData = ref({ name: '', age: 0 })
// 重新赋值依然响应式
formData.value = { name: '张三', age: 18 }
// 3. 需要传递给组合式函数或解构的数据
const { value } = useCounter() // ref 更明确
// 4. 不确定类型的动态数据
const data = ref(null) // 可能变成对象、数组、基本类型
setTimeout(() => {
data.value = { id: 1 } // 对象
// 或
data.value = 100 // 数字
}, 1000)
reactive 的场景
// 1. 复杂对象的属性修改(避免大量 .value)
const user = reactive({
name: '张三',
age: 18,
address: {
city: '北京',
street: '长安街'
}
})
// 直接访问,更简洁
user.age = 20
user.address.city = '上海'
// 2. 表单数据(多个关联字段)
const form = reactive({
username: '',
password: '',
email: '',
remember: false
})
// 3. 嵌套对象较多的数据结构
const treeData = reactive({
id: 1,
children: [
{ id: 2, children: [] }
]
})
// ❌ reactive 整体重新赋值会丢失响应式
let state = reactive({ count: 0 })
state = { count: 1 } // 失去响应式!
// ✅ 使用 Object.assign 更新
Object.assign(state, { count: 1 })
// ✅ 或改用 ref
const state = ref({ count: 0 })
state.value = { count: 1 } // 保持响应式
// ❌ reactive 解构丢失响应式
const state = reactive({ count: 0, name: 'Vue' })
let { count, name } = state
count++ // 不会触发更新
// ✅ 使用 toRefs 保持响应式
const { count, name } = toRefs(state)
count.value++ // 触发更新
// ✅ ref 解构时注意 .value
const count = ref(0)
let { value } = count // 可以,但 value 是基本类型
value++ // 不会修改原始值
// 更推荐直接使用 count.value
// 场景1:简单的计数器、开关、输入框绑定
const visible = ref(false)
const loading = ref(true)
const keyword = ref('')
// 场景2:复杂的表单(多个字段)
const formData = reactive({
username: '',
password: '',
confirmPwd: '',
phone: '',
code: ''
})
// 场景3:需要重置整个对象
const userInfo = ref(null)
const fetchUser = async () => {
const res = await api.getUser()
userInfo.value = res.data // 直接替换
}
// 场景4:从组合式函数返回响应式数据
function useCounter() {
const count = ref(0) // ref 更安全
const increment = () => count.value++
return { count, increment } // 返回 ref 可解构
}
// 场景5:响应式对象中嵌套 ref(自动解包)
const state = reactive({
count: ref(0),
name: 'Vue'
})
state.count // 自动解包,不需要 .value
reactive 适合大对象且频繁修改属性的场景,Proxy 代理性能更好
ref 的 .value 访问有轻微性能开销,但几乎可忽略
// 约定1:统一风格(推荐)
// 简单数据用 ref,复杂对象用 reactive
const count = ref(0)
const user = reactive({ name: '', age: 0 })
// 约定2:全用 ref(更一致,避免混淆)
const count = ref(0)
const user = ref({ name: '', age: 0 })
// 缺点:对象访问需要 .value.user.name
// 约定3:全用 reactive + toRefs
const state = reactive({ count: 0, user: { name: '' } })
const { count, user } = toRefs(state)
总结:没有绝对的对错,团队统一即可。大多数场景下,不确定用 ref 是最安全的选择。