Vue3中ref和reactive的对比

1. ref

定义

  • 用途: 用于创建基本数据类型或单一值的响应式引用。
  • 语法: const myRef = ref(initialValue);

特性

  • 返回一个包含 .value 属性的 Proxy 对象。
  • 适用于基本数据类型(如数字、字符串、布尔值等)和单一值。
import { ref } from 'vue';

const count = ref(0); // 创建一个响应式引用

// 访问和修改
console.log(count.value); // 0
count.value++; // 修改值
console.log(count.value); // 1

原理

  • 代理: ref 创建的 Proxy 对象拦截对 .value 属性的访问和修改,从而实现响应式。
  • 依赖追踪: 当访问 .value 时,Vue 会记录该值的依赖,以便在值变化时通知相关组件更新。

ref通过一个RefImpl实例持有原始数据,进而使用.value属性访问和更新。而对于一个实例而言,其属性值是可以修改的。因此可以通过.value的方式为ref重新分配数据,无需担心RefImpl实例被改变进而破坏响应式:

import { ref } from 'vue';

const myRef = ref({ count: 0 }); // 创建一个 ref 包含一个对象

// 访问初始值
console.log(myRef.value.count); // 输出: 0

// 修改原始数据的属性
myRef.value.count++; // 递增 count
console.log(myRef.value.count); // 输出: 1

// 重新分配新的对象
myRef.value = { count: 10 }; // 重新分配新对象
console.log(myRef.value.count); // 输出: 10

// 确保响应式仍然有效
myRef.value.count += 5; // 更新新对象的属性
console.log(myRef.value.count); // 输出: 15

RefImpl实例一直不变,只是改变其值 

2. reactive

定义

  • 用途: 用于创建对象或数组的响应式状态。
  • 语法: const myReactive = reactive(initialObject);

特性

  • 返回一个 Proxy 对象,监控整个对象的属性。
  • 适用于复杂数据结构(如对象、数组等)。
import { reactive } from 'vue';

const state = reactive({ count: 0 }); // 创建一个响应式对象

// 访问和修改
console.log(state.count); // 0
state.count++; // 修改值
console.log(state.count); // 1

原理

  • 代理: reactive 创建的 Proxy 对象监控对象的所有属性访问和修改。
  • 深度响应式: 可以监控嵌套对象和数组的变化,确保所有层级的属性都是响应式的。
  • 依赖追踪: 访问对象的属性时,Vue 会记录这些访问,以便在属性变化时更新相关的组件或计算属性。

reactive返回的是原始对象的代理,因此不能对其重新分配对象,只能通过属性访问修改属性值,否则会破坏掉响应式:

import { reactive, effect } from 'vue';

// 创建一个响应式对象
let objectReactive = reactive({ count: 0 });

// 创建副作用函数,监听数据变化
effect(() => {
  console.log(`数据变化了:${objectReactive.count}`);
});

// 正常修改值
objectReactive.count = 1; // 输出: 数据变化了:1
objectReactive.count = 2; // 输出: 数据变化了:2

// 重新分配整个对象(会导致响应式失效)
objectReactive = reactive({ count: 3 }); // 新的对象被创建,但原来的代理失效
objectReactive.count = 4; // 这不会触发 effect

// 输出结束信息
console.log("结束了");
// 输出如下:
// 数据变化了:0
// 数据变化了:1
// 数据变化了:2
// 结束了

总结对比:

watch监听:

在 Vue 3 中,watch 可以直接监听自身组件中的 ref 对象,而不需要使用 getter 函数的原因是因为 ref 本身是响应式的,Vue 会自动处理对 ref 的变化监听。

什么时候用getter函数的写法监听,什么时候直接监听

1. ref 的工作原理:

  • Proxy 对象: 当使用 ref 创建一个响应式引用时,Vue 返回的是一个 Proxy 对象。这个对象具有 .value 属性,用于存储实际的响应式值。

    const count = ref(0); // count 是一个包含 .value 的 Proxy 对象
    

2. 直接监听 ref:

  • 直接监听: 在自己的组件中,您可以直接使用 watch(refCount, callback) 来监听 refCount 的变化,因为 Vue 知道 refCount 是一个响应式对象。

    import { ref, watch } from 'vue';
    
    const refCount = ref(0);
    
    watch(refCount, () => {
      console.log(`refCount 数据变化了`);
    });
    
    // 修改 refCount 的值
    refCount.value++; // 这将触发 watch 的回调
    

3. 为什么可以直接监听:

  • 自动处理: Vue 的响应式系统会自动处理对 ref 的访问和修改,因此不需要额外使用 getter 函数。watch(refCount, ...) 会在内部处理这个 ref 的代理,并在其值变化时触发回调。

  • 依赖收集: 由于直接传递的是 ref 对象,Vue 会在执行 watch 时自动收集对这个 ref 的依赖。

4. 与 Props 的区别(父传子传递ref对象):

Props 解包: 当从父组件传递一个 ref 作为 prop 到子组件时,Vue 会解包这个 ref 的值,使得子组件接收到的是原始值(例如,数字)。此时,需要使用 getter 来监听,因为 props 是只读的。

  • watch(() => props.aa, (newValue, oldValue) => {
      console.log(`AA changed from: ${oldValue} to: ${newValue}`);
    });
    

总结

  • 直接监听 ref: 在组件内部,可以直接监听 ref 对象,因为 Vue 的响应式系统会自动处理对这个对象的代理。
  • Props 处理: 对于从父组件传递的 ref,由于它会被解包成原始值,需要使用 getter 函数来确保正确监听变化。
### Vue3 中 `ref` `reactive` 的响应式实现原理 #### 一、Vue3 响应式的整体机制 Vue3 使用了一种基于 Proxy 的全新响应式系统,取代了 Vue2 中的 Object.defineProperty 方法。Proxy 提供了一个更强大灵活的方式来拦截对象的操作行为[^1]。 当通过 `ref` 或 `reactive` 定义一个响应式变量时,Vue3 实际上会利用 JavaScript 的 Proxy 来追踪这些变量的变化,并在变化发生时通知视图更新。 --- #### 二、`ref` 的响应式实现原理 `ref` 是用来定义基本类型的响应式数据的一种方式。它内部实际上是一个包装器,将入的存储在一个带有 `.value` 属性的对象中。这样做的目的是为了使该可以被检测到其变更。 以下是其实现的关键点: - 当调用 `ref(value)` 时,如果递的是一个对象,则会自动将其转换为 `reactive` 类型;如果是原始类型(如字符串、数字等),则保持不变。 - 在模板或其他地方访问 `ref` 数据时,Vue 自动解包 `.value`,使得开发者无需显式书写 `.value` 即可获取实际[^3]。 代码示例展示如何使用 `ref` 并观察其工作流程: ```javascript import { ref } from 'vue'; const count = ref(0); // 创建一个 ref 对象 console.log(count.value); // 输出初始 0 count.value++; // 修改 .value 属性触发响应式更新 console.log(count.value); // 输出修改后的 1 ``` --- #### 三、`reactive` 的响应式实现原理 `reactive` 主要用于处理复杂的嵌套对象结构。它的核心是通过 Proxy 拦截对对象属性的各种操作(读取、设置、删除等)。具体来说: - 调用 `reactive(obj)` 后返回的新对象是对原对象的一个代理版本。 - 如果尝试访问或更改此代理对象上的任何属性,都会触发相应的 getter setter 钩子函数。 - 这些钩子函数负责记录依赖关系以及后续的通知过程。 下面是一段简单的例子说明 `reactive` 的功能: ```javascript import { reactive } from 'vue'; const state = reactive({ name: 'Alice', age: 25, }); state.age += 1; // 更改年龄属性触发响应式更新 console.log(state.name, state.age); // 打印 Alice 新的年龄 ``` 需要注意的一点是,由于 `reactive` 返回的是一个新的代理实例,因此不能直接对其源对象进行赋或者比较相等问题。 --- #### 四、两者的对比分析 尽管两者都服务于相同的最终目标——建立响应式链接,但在某些方面存在差异: - **适用范围**: - `ref`: 推荐用于简单基础类型的数据声明; - `reactive`: 更适合管理具有多层嵌套特性的复杂状态树。 - **性能开销**: - `ref` 添加额外一层封装 (`.value`) ,可能稍微增加内存消耗; - `reactive` 则直接作用于整个对象层次之上,通常效率更高一些。 总结而言,在选择工具之前应该考虑具体的业务需求个人偏好等因素再做决定。 --- ### 结论 无论是采用 `ref` 抑或是 `reactive` 方式来构建应用中的动态交互逻辑,背后均依托着强大的 Proxy 技术支持完成高效的双向绑定效果。理解清楚各自的特性有助于我们写出更加优雅简洁且易于维护的应用程序代码[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值