官方文档的一些话
先贴官方发言🌱:
官方文档的一些话的总结:
推荐使用 ref() 作为声明响应式状态的主要API。
reactive 的局限性:仅对对象类型有效,且重新赋值会丢失响应性。
对官方的话的一些理由支撑
在 Vue 3 的 Composition API 中,ref 和 reactive 都是创建响应式数据的核心方法,为什么官方推荐使用 ref 而不是 reactive呢?
核心对比
| 特性 | reactive | ref |
|---|---|---|
| 支持数据类型 | ❌ 仅对象/数组(引用类型) | ✅ 基本类型+引用类型 |
| 模板中使用 | ✅ 直接使用 | ✅ 自动解包(.value 不需要) |
| script 中使用 | ✅ 直接访问属性 | ❌ 需要 .value 访问 |
| 重新赋值 | ❌ 会丢失响应性 | ✅ 保持响应性 |
| 传入函数 | ❌ 可能丢失响应性 | ✅ 保持响应性 |
| 解构 | ❌ 会丢失响应性(需toRefs) | ❌ 对象解构会丢失响应性 |
推荐 ref 的主要原因
- 更广泛的数据类型支持
ref可以处理任何类型的数据(基本类型和引用类型)
reactive仅限于对象和数组
reactive只能声明引用数据类型。
let obj = reactive({
name: 'jack',
age: 18
})
ref除了基本类型,引用类型也可以,全部类型都可:
// 对象和数组
const a = ref({})
const b = ref([])
- 更稳定的响应性
reactive在重新赋值整个对象时会丢失响应性
ref通过.value重新赋值不会丢失响应性
丢:
let state = reactive({ count: 0 })
state = { count: 1 } // 这样赋值 丢失响应
// 正确✅做法是:
state.count = 1
// 或者用 Object.assign
state = Object.assign(state, {count: 1})
// 或者咱不用reactive,用ref
let state = ref({ count: 0 })
state.value = { count: 1 }
也丢:
<template>
{{ state }}
</template>
const state = reactive({ count: 0 })
nextTick(() => {
state = reactive({ count: 1 }); // 响应式已经丢失了,就算是nextTick也没用
})
原理区别:
ref 通过包装值为引用对象,而不是原始值的引用。
访问和修改的都是.value属性,无论赋值还是传递都保持响应式。
reactive的属性若单独赋值给变量,相当于深拷贝字面量,与原属性断开响应连接;
修改该变量不会影响原对象,因其不再是代理的响应式属性。
简言之:
ref的响应性由.value维护,稳定。reactive的响应性依赖原对象引用,替换整体或解构属性会破坏响应。
-
更一致的开发体验
虽然需要.value,但这也是一种显式标识
配合 Volar 插件可自动补全.value,减少输入负担 -
更少的心智负担
不需要记住reactive的各种边界情况
不需要频繁使用toRefs来保持解构后的响应性
reactive 的痛点示例
// 1. 重新赋值问题
let state = reactive({ count: 0 })
state = { count: 1 } // 失去响应性
// 2. 解构问题
let { count } = reactive({ count: 0 }) // count 不再响应式
// 3. 类型限制
let num = reactive(0) // 无效!只能用于对象
ref 的优势示例
// 1. 基本类型
const num = ref(0) // 正常工作
// 2. 对象类型
const state = ref({ count: 0 })
state.value = { count: 1 } // 保持响应性
// 3. 模板中自动解包
// <div>{{ count }}</div> 不需要 .value
何时使用 reactive?
虽然推荐 ref 作为主要选择,但 reactive 仍然在以下场景有其价值:
- 需要直接操作复杂嵌套对象且不打算重新赋值时
- 与需要普通对象的第三方库集成时
- 当组件的本地状态是一个明确的对象时(如表单)