Vue3 中 ref 与 reactive 的区别及底层原理详解

一、核心区别

1. 数据类型与使用场景

• ref

 可定义基本类型(字符串、数字、布尔值)和对象类型的响应式数据。对于对象类型,`ref` 内部会自动调用 `reactive` 将其转换为响应式对象。  
 语法特点:需通过 `.value` 访问或修改数据(模板中自动解包,无需 `.value`)。  
 适用场景:简单数据、需跨组件传递的独立变量、需要重新赋值的场景(如替换整个对象)。  

• reactive

 仅支持对象类型(对象、数组、Map/Set 等),通过 `Proxy` 实现深度响应式代理。  
 语法特点:直接访问属性(如 `state.count`),无需 `.value`,但无法直接替换整个对象(需用 `Object.assign` 合并更新)。  
 适用场景:复杂嵌套对象、需深度响应式追踪的复杂数据结构。  
2. 响应式机制差异
ref 底层原理
 通过封装对象的 `.value` 属性实现响应式:  
 ◦ 对基本类型使用 `Object.defineProperty` 的 `get/set` 进行数据劫持。  

 ◦ 对对象类型内部调用 `reactive` 转换为 `Proxy` 代理。  

 ```javascript
 // 简化的 ref 实现逻辑
 function ref(value) {
   return {
     get value() { track(this, 'value'); return value; },
     set value(newVal) { value = newVal; trigger(this, 'value'); }
   };
 }
 ```
reactive 底层原理
 基于 `Proxy` 拦截对象属性的增删改查,结合 `Reflect` 操作原始数据:  
 ```javascript
 // 简化的 reactive 实现逻辑
 function reactive(obj) {
   return new Proxy(obj, {
     get(target, key) { track(target, key); return Reflect.get(target, key); },
     set(target, key, value) { 
       Reflect.set(target, key, value); 
       trigger(target, key); 
       return true;
     }
   });
 }
 ```
 所有嵌套属性均会被递归代理,实现深层响应性。

二、关键特性对比

特性refreactive
数据类型基本类型 + 对象类型仅对象类型
访问方式.value(模板自动解包)直接访问属性(如 state.key
重新赋值支持(通过 .value =需合并更新(如 Object.assign
解构响应性解构后仍需 .value解构会丢失响应性,需 toRefs
性能基本类型更轻量复杂对象更高效(Proxy 深度监听)

三、设计理念与使用建议

1. 设计哲学

ref 提供单一值响应式的原子化封装,适合组件间传递独立状态。

reactive 针对复杂状态树设计,通过 Proxy 实现细粒度依赖追踪,优化深层更新性能。

2. 使用建议

• 优先 ref 的场景:

 ◦ 简单数据(如计数器、表单字段)。  

 ◦ 需要频繁替换整个对象(如接口返回数据更新)。  

• 优先 reactive 的场景:

 ◦ 复杂配置对象(如含多层嵌套的表单数据)。  

 ◦ 需要自动追踪属性增删的场景(如动态表单字段)。  
3. 注意事项

reactive 直接替换整个对象会丢失响应性,需用 Object.assign 合并更新。

• 模板中 ref 对象自动解包,但 JavaScript 中必须使用 .value

• 使用 toRefs 解构 reactive 对象可保持响应性。


四、总结

refreactive 是 Vue3 响应式系统的两大核心 API:
ref 通过 .value 封装简化基本类型响应式,兼顾对象类型的灵活性。

reactive 利用 Proxy 实现深度监听,适合复杂状态管理。

开发者应根据数据类型、更新频率及使用场景选择最合适的 API,必要时结合 toRefs 优化代码结构。


异步输出

async await后面的任务是会加入微任务队列,本身是同步函数的话直接就console.log()


this指向输出

const定义变量时不会被挂载到window上


refreactive 是 Vue 3 中构建响应式数据的两大核心 API。虽然它们在使用上相似,但底层机制和适用场景有明显不同。


🧠 一句话理解

  • ref():用于包装基本类型(也可包装对象),返回一个响应式对象,值通过 .value 访问。
  • reactive():用于将对象转换为响应式对象,是基于 Proxy 实现的深层响应式代理

⚙️ 底层机制对比

特性refreactive
适用数据类型基本类型 / 对象只能用于对象(数组、Map、Set、纯对象)
响应式实现方式使用 Object.defineProperty 包装 .value 属性(浅层响应)使用 Proxy 对原对象进行深层代理
访问方式通过 .value 访问/赋值直接使用对象属性
是否深度响应否(但 ref({}) 内部值可递归为 reactive是(默认深度响应)
是否可自动解包是(在 <script setup>模板中)否(属性不需要 .value,但不能自动解包嵌套 ref

🔍 底层原理简要(源码角度)

ref 原理(简化示意):

function ref(value) {
  return {
    __v_isRef: true,
    get value() {
      track() // 依赖收集
      return value
    },
    set value(newVal) {
      value = newVal
      trigger() // 响应式触发
    }
  }
}

ref 会为 value 属性设置 getter/setter,用于追踪依赖和更新响应。


reactive 原理(简化示意):

function reactive(target) {
  return new Proxy(target, {
    get(obj, key) {
      track(obj, key) // 依赖收集
      return Reflect.get(obj, key)
    },
    set(obj, key, value) {
      const result = Reflect.set(obj, key, value)
      trigger(obj, key) // 触发更新
      return result
    }
  })
}

reactive 会使用 Proxy 监听整个对象,对所有属性深层响应。


✅ 什么时候用 ref,什么时候用 reactive

场景使用方式
管理一个单一的基本类型变量(如 countref(0)
管理一个对象或数组reactive({})
需要手动 .value 引用使用 ref
结构清晰、字段多的响应式对象reactive 更方便

🧪 示例对比

const count = ref(0)
count.value++  // ✅

const state = reactive({ count: 0 })
state.count++  // ✅

也可以这样混用:

const state = reactive({
  count: ref(0)  // 嵌套 ref
})
state.count.value++

🧬 组合推荐

在实际项目中,Vue 推荐你:

  • 基础数据用 ref
  • 结构性数据用 reactive
  • 如有嵌套响应性需求,可以混合使用

如果你想深入源码理解 refreactive 在响应式系统中的角色,我可以进一步讲解 Vue 3 中的 effect.tsreactivity.ts 模块。是否需要?

<think>嗯,用户问的是Vue3refreactive的实现原理是否相同,以及各自的原理是什么。我需要先回忆一下Vue3的响应式系统是怎么工作的。Vue3主要用Proxy来实现响应式,但refreactive可能有所不同。 首先,reactive是用于对象类型的响应式转换,它确实基于Proxy。当用reactive包裹一个对象时,Vue会创建一个Proxy来拦截对对象属性的访问修改,这样就可以跟踪依赖触发更新。对于基本类型,比如字符串、数字,reactive不适用,这时候就需要用ref。 而ref可以处理基本类型对象。对于基本类型,ref内部会用一个对象来包装值,这个对象有一个value属性。当访问ref的value时,会触发getter,收集依赖;修改value时,触发setter,通知更新。如果ref包裹的是对象,实际上内部会调用reactive来转换这个对象,所以这种情况下ref的value属性是一个Proxy。 所以两者的实现原理不同,但ref在处理对象时依赖了reactive的Proxy机制。可能用户会混淆这两者的使用场景,比如为什么有时候需要用.value,有时候不需要。另外,用户可能想知道为什么需要两种不同的API,而不是统一使用其中一个。 需要明确回答它们的实现差异,并给出示例代码。比如,展示reactive处理对象,而ref处理基本类型,以及ref处理对象时内部使用reactive的情况。然后,相关问题可以涉及使用场景、.value的必要性、性能差异以及如何选择两者等。</think><< Vue3中的`ref``ref``reactive`的实现原理**不完全相同**,虽然它们都基于响应式系统,但底层机制有明显差异。 ### 1. `reactive`的实现原理 - **核心机制**:基于ES6的`Proxy`实现,用于**对象类型**的响应式转换。 - **工作流程**: 1. 调用`reactive(obj)`时,会为`obj`创建一个`Proxy`代理对象。 2. `Proxy`会拦截所有对对象属性的操作(`get`、`set`、`deleteProperty`等)。 3. 在`get`时收集依赖(通过`track`函数),在`set`时触发更新(通过`trigger`函数)。 - **示例代码**: ```javascript const obj = reactive({ count: 0 }); // 底层会生成类似如下的Proxy: const proxy = new Proxy(obj, { get(target, key, receiver) { track(target, key); // 依赖收集 return Reflect.get(target, key, receiver); }, set(target, key, value, receiver) { Reflect.set(target, key, value, receiver); trigger(target, key); // 触发更新 return true; } }); ``` ### 2. `ref`的实现原理 - **核心机制**:基于`RefImpl`类实现,主要用于**基本类型**(如`number`、`string`),也可用于对象。 - **工作流程**: 1. 调用`ref(value)`时,会创建一个`RefImpl`实例。 2. 如果传入的是对象,内部会调用`reactive`将其转换为响应式对象。 3. 通过`.value`属性访问时触发`get`,修改时触发`set`。 - **示例代码**: ```javascript const num = ref(0); // 底层实现类似: class RefImpl { constructor(value) { this._value = isObject(value) ? reactive(value) : value; } get value() { track(this, &#39;value&#39;); // 依赖收集 return this._value; } set value(newVal) { this._value = newVal; trigger(this, &#39;value&#39;); // 触发更新 } } ``` ### 关键区别总结 | 特性 | `reactive` | `ref` | |--------------------|---------------------------------|--------------------------------| | 适用类型 | 对象/数组 | 基本类型 + 对象 | | 访问方式 | 直接访问属性(`obj.key`) | 通过`.value`(`refValue.value`)| | 底层实现 | `Proxy` | `RefImpl`类 + 可能嵌套`Proxy` | ### 何时选择? - 使用`reactive`处理深层嵌套的对象/数组。 - 使用`ref`处理基本类型或需要明确控制`.value`的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GISer_Jinger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值