Vue3中reactive和ref的区别

1. 定义方式与基础类型支持

  • reactive:用于创建响应式对象(包括数组、Map、Set 等)。

import { reactive } from 'vue'
const state = reactive({ count: 0 })
  1. 局限性:无法直接处理基本类型(如number,string,boolean)。reactive 的本质是通过 ES6 Proxy 拦截对象属性的访问和修改。但基本类型(如数字、字符串)不是对象,没有属性可供拦截,因此无法直接转为响应式。
  2. 深层响应式:对象的所有嵌套属性都会被转为响应式。当 reactive 处理对象时,不仅对象本身,其所有嵌套层级的属性都会被递归转为响应式。这意味着 Vue 能追踪任何深度的属性变化。 
  • ref:用于创建任意类型的响应式引用,包括基本类型。

import { ref } from 'vue'
const count = ref(0)
  1. 本质:内部将值包装为一个对象,通过.value属性访问。基本类型(如 numberstring)没有属性可供拦截,而 ref 通过创建一个包含 .value 属性的对象,利用 Object.defineProperty 的 getter/setter 实现响应式。
import { ref } from 'vue'

const count = ref(0)

// 本质上,count 是这样一个对象:
// {
//   value: 0,
//   // 内部有 getter/setter 实现响应式
//   get value() { /* 追踪依赖 */ },
//   set value(newVal) { /* 触发更新 */ }
// }

// 修改值时必须通过 .value
count.value = 1 // 触发响应式更新
  1. 浅层响应式:仅.value是响应式的,若值为对象需使用reactive嵌套。ref 的响应式特性仅作用于 .value 属性,若 .value 是对象或数组,需要手动用 reactive 嵌套才能实现深层响应式。
const user = ref({ name: 'John' })

// ✅ 修改 .value 触发更新
user.value = { name: 'Jane' } // 整个对象替换

// ❌ 直接修改对象内部属性,非响应式!
user.value.name = 'Jane' // 虽然值变了,但 Vue 无法追踪这个变化

// ✅ 正确做法:用 reactive 嵌套
const user = ref(reactive({ name: 'John' }))
user.value.name = 'Jane' // 现在可以响应式更新
  • 灵活性:允许 .value 动态变更类型(如从 number 变为 object)。
const value = ref(0) // 初始为数字

// 可以动态变更为对象
value.value = { name: 'Vue' } // 仍然保持响应式
  • 性能优化:避免不必要的深层递归,按需使用 reactive 控制响应式深度。

 2. 访问方式

  • reactive:直接访问对象属性。
console.log(state.count) // 无需.value
  • ref:在模板中自动解包,在 JavaScript 中需通过.value
// 模板中
<div>{{ count }}</div> // 自动解包,无需.value

// JavaScript中
console.log(count.value) // 需要.value

 3. 响应式原理

  • reactive:基于 ES6 Proxy 实现,拦截对象的属性访问和修改。
  1. 深层响应式:自动递归处理所有嵌套对象。
  2. 更全面的监听:能监听属性的添加、删除、枚举等操作。
  3. 更好的性能:相比 Vue2 的 Object.defineProperty,减少了初始化时的递归遍历开销。
  • ref:本质是一个包含.value属性的对象,通过Object.definePropertygetter/setter实现响应式。

 4. 使用场景

  • reactive
  1. 处理复杂对象或嵌套结构。
  2. 状态管理(如 Vuex/Pinia 的替代方案)。
  3. 示例:表单数据、组件状态对象。
  • ref
  1. 基本类型响应式数据。
  2. 模板中的变量(如计数器、加载状态)。
  3. 与 DOM 交互(通过ref属性)。
  4. 示例:计数器、表单输入值、API 加载状态。

5. 注意事项

  • 解构赋值丢失响应式reactive对象解构后会失去响应性,需使用toRefs保持。toRefs 的本质是为每个属性创建一个 ref,这个 ref 的 getter/setter 会代理到原始对象的属性上。
const state = reactive({
          count: 0
        });

        // ❌ 解构赋值导致响应性丢失
        const { count } = state;

        const increment = () => {
          state.count++;
          console.log('state.count:', state.count);
          console.log('count:', count); // 解构后的 count 不会更新
        };

 不是同一个对象,但值始终保持同步(下图)

const state = reactive({
          count: 0
        });
        
        const { count } = toRefs(state);
        
        const checkRelationship = () => {
          // 验证 state.count 和 count.value 是否指向同一值
          console.log('state.count === count.value:', state.count === count.value); // true
          
          // 验证 count 是否是 ref 对象
          console.log('count 是否是 ref 对象:', Vue.isRef(count)); // true
          
          // 验证 state.count 是否是 ref 对象
          console.log('state.count 是否是 ref 对象:', Vue.isRef(state.count)); // false
          
          // 修改 state.count,观察 count.value
          state.count++;
          console.log('修改后: state.count =', state.count, 'count.value =', count.value);
          
          // 修改 count.value,观察 state.count
          count.value++;
          console.log('再修改后: state.count =', state.count, 'count.value =', count.value);
        };
        
  • 数组和对象优先用reactiveref适合简单值,复杂结构用reactive更自然。

 6. 选择建议

  • 使用ref
  1. 数据类型可能变化(如从number变为object)。
  2. 处理基本类型。
  3. 需要与 Vue 的模板系统直接交互。
  • 使用reactive
  1. 处理对象、数组等复杂结构。
  2. 需要深层响应式。
  3. 避免频繁使用.value

总结:

ref是通用解决方案,适合所有类型;reactive更适合复杂对象,避免.value的繁琐。根据场景灵活选择,可以混合使用以达到最佳效果。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值