本文共 2516 字,大约阅读时间需要 8 分钟。
Vue3 中的响应式数据处理通过两个核心 API——ref 和 reactive ——实现。虽然两者都能用于管理响应式状态,但它们的实现方式和适用场景有本质区别。本文将从源码角度深入分析两者的工作原理,并探讨为什么 Vue3 官方推荐使用 ref 而非 reactive。
ref 是一个函数,接受一个内部值并返回一个响应式且可变的引用对象。这个引用对象有一个 .value 属性,指向内部值。ref 的核心在于返回一个 RefImpl 实例,这个实例通过 getter 和 setter 方法实现了响应式的数据追踪和更新。
RefImpl 类的构造函数接受一个初始值,并创建一个包含以下组件的对象:
_value):存储传递的初始值。_dep):通过 Dep 类管理依赖关系,用于追踪和通知所有依赖于当前 Ref 对象的效果(如计算属性或副作用函数)。toReactive 函数将值转换为响应式对象。对于基础数据类型,直接返回原始值;对于对象和数组,则通过 reactive 创建响应式代理后再包装。RefImpl 的 value getter 方法通过 trackRefValue 进行依赖收集,确保当 Ref 的值发生变化时,依赖它的组件或副作用函数能够重新运行。value setter 方法则通过 set 方法触发更新,通知所有依赖并更新相关状态。
import { ref } from 'vue';let state = ref({ count: 0 });state.value.count++; reactive 函数通过创建一个 Proxy 对象来实现响应式数据的代理。Proxy 会拦截对目标对象的操作,从而实现对数据变化的响应式追踪。源码中,reactive 的实现步骤如下:
createReactiveObject 函数创建一个 Proxy 实例,并注册相关的依赖和更新机制。尽管 reactive 通过 Proxy 实现了响应式,但它存在一些重要的局限性:
仅适用于引用数据类型:reactive 主要针对对象(包括数组和集合)有效,对基础数据类型(如 string、number 和 boolean)无效。
import { reactive } from 'vue';const state = reactive({ count: 0 });// count 为响应式属性 如果尝试用基础数据类型:
import { reactive } from 'vue';const state = reactive(123);// state 是非响应式对象 赋值和解构操作容易失去响应性:
import { reactive } from 'vue';let state = reactive({ count: 0 });state = { count: 1 };// 失去响应性 import { reactive } from 'vue';const state = reactive({ count: 0 });let { count } = state;count++; // count 仍为 0 属性赋值会失去响应性:
import { reactive } from 'vue';let state = reactive({ count: 0 });let count = state.count; // count 为非响应式变量 ref 提供了一种更加统一和灵活的响应式解决方案,避免了 reactive 的局限性。以下是推荐使用 ref 的几个关键原因:
ref 适用于所有数据类型,包括基础数据类型和复杂对象,简化了开发者的代码书写。 import { ref } from 'vue';let num = ref(0);let str = ref('Hello');let obj = ref({ count: 0 }); ref 支持对嵌套对象和数组的深层追踪和更新。 import { ref } from 'vue';let obj = ref({ user: { details: { age: 18 } } });obj.value.user.details.age++; ref 在处理普通赋值和复杂操作方面表现优异,支持对象的替换、属性的增删和动态更新。 import { ref } from 'vue';let state = ref({ count: 0, name: 'Vue' });state.value = { count: 10, name: 'Vue 4' };state.value.count = 20;state.value.name = 'Vue 5';state.value.newProperty = 'New Property';delete state.value.newProperty; ref 的实现简单明了,避免了 reactive Proxy 的性能开销和复杂性。ref 在 Vue3 中提供了一种更统一、灵活的响应式解决方案,适用于所有类型的数据。相比之下,reactive 虽然也能实现响应式,但在数据类型局限性和赋值操作上存在明显不足。因此,推荐使用 ref 来处理 Vue3 的响应式数据,既简化了代码编写,也提升了开发体验。
转载地址:http://wunfk.baihongyu.com/