在 Vue 3 中,ref 和 reactive 是 Composition API 提供的两个核心响应式 API,用于创建响应式状态。它们都基于 JavaScript 的 Proxy(reactive)和 getter/setter(ref 的内部机制)来实现响应式追踪,但在使用场景和行为上有一些关键区别。
1. ref
用途
- 用于定义基本数据类型 (如
string,number,boolean)的响应式数据。 - 也可以用于定义对象或数组 ,此时内部会自动调用
reactive。 - 在模板中使用时,无需
.value,Vue 会自动解包。 - 在 JavaScript 中访问或修改时,必须通过
.value。
示例
csharp
import { ref } from 'vue'
const count = ref(0)
console.log(count.value) // 0
count.value++
xml
<template>
<p>{{ count }}</p> <!-- 自动解包,无需 .value -->
<button @click="count++">增加</button>
</template>
特点
- 适用于任何类型。
- 对于对象/数组,
ref内部会调用reactive。 - 可以通过
ref()创建对对象的响应式引用,保留其引用身份(identity)。
2. reactive
用途
- 专门用于定义对象或数组类型的响应式数据。
- 不能用于基本数据类型 (如
number,string)。 - 返回的是一个代理对象(Proxy),直接操作其属性即可,不需要
.value。
示例
php
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: { name: 'Alice' }
})
state.count++
state.user.name = 'Bob'
xml
<template>
<p>{{ state.count }}</p>
<p>{{ state.user.name }}</p>
</template>
特点
-
只能用于对象/数组。
-
返回的是原始对象的代理,无法替换整个对象(否则失去响应性)。
ini// ❌ 错误做法:直接赋值新对象会丢失响应性 state = { count: 1 } // ✅ 正确做法:修改属性 state.count = 1
对比总结
| 特性 | ref |
reactive |
|---|---|---|
| 支持类型 | 任意类型(基本类型 + 对象/数组) | 仅对象或数组 |
| 访问方式(JS 中) | 需 .value |
直接访问属性 |
| 模板中使用 | 自动解包,无需 .value |
直接访问 |
| 替换整个对象 | 可以(重新赋值 .value) |
不可以(会丢失响应性) |
| 内部实现 | 基本类型用 getter/setter;对象用 reactive |
基于 Proxy |
| 适用场景 | 简单值、需要替换整个对象的情况 | 复杂对象状态管理 |
最佳实践建议
- 优先使用
ref:尤其在 TypeScript 项目中,ref的类型推断更直观,且统一使用.value有助于代码一致性。 - 当你有一个复杂的对象状态,并且不需要替换整个对象时,可以使用
reactive。 - 避免混用导致困惑。例如,不要在一个
reactive对象中嵌套ref除非必要(虽然 Vue 会自动解包,但可能影响可读性)。
补充:toRefs 和 toRef
当你从 reactive 对象中解构属性时,会丢失响应性。此时可使用 toRefs 或 toRef:
javascript
import { reactive, toRefs } from 'vue'
const state = reactive({ count: 0 })
const { count } = toRefs(state) // count 是一个 ref
count.value++ // 仍然响应式