Vue3 Composition API 中,ref 和 reactive 是创建响应式数据的核心 API。
ref 可以包装任何类型的值,通过 .value 访问(模板自动解包),适合基本类型和需要重新赋值的变量;
reactive 专为对象/数组设计,实现深层响应式,适合复杂数据结构。
主要区别在于数据类型支持、访问方式和重新赋值能力。
实际开发中,建议基本类型用 ref,复杂对象用 reactive,组合函数返回多值时优先使用 ref 以便解构。
两者性能相当,选择应基于代码组织和开发体验。
ref 和 reactive 是 Vue 3 Composition API 中创建响应式数据的两个核心 API。
我们来深入解析它们的特点、区别和使用场景。
核心概念
1. reactive - 创建对象/数组的响应式代理
reactive 接收一个普通对象或数组,返回该对象的响应式代理(Proxy)。
javascript
import { reactive } from 'vue'
// 创建响应式对象
const state = reactive({
count: 0,
user: {
name: '张三',
age: 25
},
todos: ['学习 Vue', '写代码']
})
// 修改数据会自动触发更新
state.count++ // ✅ 响应式
state.user.name = '李四' // ✅ 响应式(深层响应)
state.todos.push('新任务') // ✅ 响应式
特点:
-
专为对象类型(Object、Array、Map、Set)设计
-
深层响应式:嵌套对象的所有层级都会变成响应式
-
返回的是原始对象的 Proxy 代理,修改代理会影响原始对象
-
直接解构会丢失响应式(需要配合
toRefs)
2. ref - 创建任意类型的响应式引用
ref 可以包装任何类型的值,使其变成响应式。
javascript
import { ref } from 'vue'
// 创建响应式值
const count = ref(0) // 包装基本类型
const user = ref({ name: '张三' }) // 包装对象
const list = ref(['a', 'b', 'c']) // 包装数组
// 访问值需要 .value
console.log(count.value) // 0
count.value++ // ✅ 响应式更新
// 在模板中自动解包(不需要 .value)
// <div>{{ count }}</div>
特点:
-
可以包装任何类型的值(String、Number、Boolean、Object、Array等)
-
返回一个响应式对象 ,只有一个
.value属性 -
在 JavaScript 中访问/修改需要
.value -
在 模板中自动解包 ,不需要
.value -
对象类型内部其实是调用了
reactive
关键区别对比
| 特性 | reactive |
ref |
|---|---|---|
| 数据类型 | 只接受对象类型(Object、Array等) | 接受任何类型 |
| 访问方式 | 直接访问属性 | 需要 .value(模板中自动解包) |
| 解构响应式 | 解构会丢失响应式,需用 toRefs |
本身就是引用,解构仍响应 |
| 重新赋值 | 不能直接重新赋值整个对象 | 可以通过 .value 重新赋值 |
| 模板使用 | 直接使用属性名 | 自动解包,直接使用变量名 |
| 适用场景 | 复杂的状态对象 | 基本类型、需要重新赋值的响应式变量 |
常见问题和解决方案
问题1:reactive 解构丢失响应式
javascript
const state = reactive({ count: 0, name: 'Vue' })
// ❌ 错误:解构会丢失响应式
const { count, name } = state
count++ // 不会触发更新
// ✅ 正确:使用 toRefs 保持响应式
import { toRefs } from 'vue'
const { count, name } = toRefs(state)
count.value++ // ✅ 保持响应式
问题2:什么时候用 ref,什么时候用 reactive?
使用 ref 的场景:
javascript
// 1. 基本类型值
const count = ref(0)
const name = ref('张三')
const isLoading = ref(false)
// 2. 需要重新赋值的对象
const user = ref({ name: '张三' })
// 整个替换(reactive 做不到)
user.value = { name: '李四' }
// 3. 模板引用(DOM 元素)
const inputRef = ref(null)
// <input ref="inputRef">
// 4. 函数返回多个响应式值时
function useFeature() {
const data = ref(null)
const error = ref(null)
return { data, error } // 方便解构
}
使用 reactive 的场景:
javascript
// 1. 复杂的表单对象
const form = reactive({
username: '',
password: '',
profile: {
age: 0,
address: ''
}
})
// 2. 全局状态管理(类似 Vuex)
const store = reactive({
user: null,
token: '',
isAuthenticated: false
})
// 3. 紧密相关的数据集合
const pagination = reactive({
page: 1,
pageSize: 10,
total: 0
})
问题3:ref 包装对象 vs reactive 直接使用
javascript
// 方式1:使用 ref 包装对象
const userRef = ref({
name: '张三',
profile: { age: 25 }
})
// 访问:userRef.value.name
// 重新赋值:userRef.value = { name: '李四' }
// 方式2:使用 reactive 创建对象
const userReactive = reactive({
name: '张三',
profile: { age: 25 }
})
// 访问:userReactive.name
// 重新赋值:不能直接 userReactive = {...}
// 实际效果:两者内部都是 Proxy,都能深层响应
// 主要区别在于 API 使用方式
最佳实践建议
1. 统一风格
在团队中约定一致的使用规则,例如:
-
表单数据使用
reactive -
单个状态值使用
ref -
函数返回的多个值使用
ref(方便解构)
2. 组合式函数推荐使用 ref
javascript
// 推荐:返回 ref,方便使用者解构
function useCounter() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
}
// 使用
const { count, increment } = useCounter()
3. 使用 TypeScript 时
TypeScript
import { ref, reactive } from 'vue'
// ref 可以推断类型
const count = ref<number>(0) // Ref<number>
const user = ref<User>({ name: '' }) // Ref<User>
// reactive 也可以
const state = reactive<{
count: number
user: User | null
}>({
count: 0,
user: null
})
4. 实际开发中的常见模式
javascript
import { ref, reactive, computed, watch } from 'vue'
export default {
setup() {
// 单个状态值
const loading = ref(false)
const error = ref(null)
// 复杂状态对象
const formState = reactive({
username: '',
password: '',
remember: false
})
// 计算属性(自动追踪依赖)
const isFormValid = computed(() => {
return formState.username && formState.password
})
// 侦听器
watch(
() => formState.username,
(newName) => {
console.log('用户名变化:', newName)
}
)
// 方法
const submitForm = async () => {
loading.value = true
try {
// 提交逻辑
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
return {
loading,
error,
formState,
isFormValid,
submitForm
}
}
}
总结
ref 和 reactive 是 Vue 3 响应式系统的基石:
-
ref:更通用,适合包装任何类型的值,特别是基本类型和需要重新赋值的变量 -
reactive:专为对象设计,语法更简洁,适合复杂的嵌套对象
记住一个简单的选择原则:
-
如果是单个值 (字符串、数字、布尔值) → 用
ref -
如果是相关数据的集合 (对象、表单) → 用
reactive -
如果不确定或需要灵活性 → 用
ref(因为可以包装对象)
两者在性能上没有本质差异,选择主要基于开发体验和代码组织。随着使用经验的增加,你会自然而然地知道在什么场景下选择哪个更合适。