vue3定义数据到底是用ref()还是reactive()❓

552 阅读3分钟

官方文档的一些话

先贴官方发言🌱:

image.png

image.png

官方文档的一些话的总结:

推荐使用 ref() 作为声明响应式状态的主要API

reactive 的局限性:仅对对象类型有效,且重新赋值会丢失响应性。


对官方的话的一些理由支撑

在 Vue 3 的 Composition API 中,refreactive 都是创建响应式数据的核心方法,为什么官方推荐使用 ref 而不是 reactive呢?

核心对比

特性reactiveref
支持数据类型❌ 仅对象/数组(引用类型)✅ 基本类型+引用类型
模板中使用✅ 直接使用✅ 自动解包(.value 不需要)
script 中使用✅ 直接访问属性❌ 需要 .value 访问
重新赋值❌ 会丢失响应性✅ 保持响应性
传入函数❌ 可能丢失响应性✅ 保持响应性
解构❌ 会丢失响应性(需toRefs)❌ 对象解构会丢失响应性

推荐 ref 的主要原因

  1. 更广泛的数据类型支持
    ref 可以处理任何类型的数据(基本类型和引用类型)
    reactive 仅限于对象和数组

reactive只能声明引用数据类型

let obj = reactive({
  name: 'jack',
  age: 18
})

ref除了基本类型,引用类型也可以,全部类型都可:

// 对象和数组

const a = ref({})

const b = ref([])
  1. 更稳定的响应性
    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的响应性依赖原对象引用,替换整体或解构属性会破坏响应。
  1. 更一致的开发体验
    虽然需要 .value,但这也是一种显式标识
    配合 Volar 插件可自动补全 .value,减少输入负担

  2. 更少的心智负担
    不需要记住 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 仍然在以下场景有其价值:

  1. 需要直接操作复杂嵌套对象且不打算重新赋值时
  2. 与需要普通对象的第三方库集成时
  3. 当组件的本地状态是一个明确的对象时(如表单)