前端学习笔记-vue中ref和reactive对比
没问题,去掉修饰,我们直接看底层的技术事实。
Vue 3 的响应式核心是利用 JavaScript 的Proxy对象来拦截数据的读取和修改。理解ref和reactive的区别,本质上是理解它们如何处理基本类型与引用类型(对象/数组)的局限性。
1. 定义数据类型的区别
reactive(obj):它的底层直接使用new Proxy(obj, ...)。因为Proxy的第一个参数必须是对象,所以reactive只能接收对象、数组、Map、Set 等引用类型。如果传入数字、字符串、布尔值,Vue 会直接报错或警告。ref(value):它可以接收任何类型的值。- 如果传入基本类型,Vue 会在内部创建一个拥有
.value属性的对象,通过get value()和set value()(即Object.defineProperty)来拦截对该属性的访问。 - 如果传入的是对象,
ref会在内部自动调用reactive来处理这个.value,也就是说ref.value此时变成了一个 Proxy 对象。
2. 内存地址替换(重新赋值)的区别
这是开发中最核心的逻辑区别。
reactive无法承受整体覆盖
当你执行reactive时,Vue 将响应式拦截器绑定到了当前的内存地址上。
letstate=reactive({count:1})// 错误操作:这改变了 state 指向的内存地址// 新的匿名对象没有被 Proxy 包装,原有的响应式链路断开state={count:2}ref支持整体覆盖
ref变量指向的内存地址永远是那个特殊的响应式包裹对象(RefImpl)。
conststate=ref({count:1})// 正确操作:修改的是包装对象的 value 属性,而不是替换 state 本身// 底层会自动将新对象转为 reactive,响应式正常工作state.value={count:2}3. 结构与参数传递中的响应式丢失
JavaScript 中基本类型的赋值是值传递,而对象的赋值是引用传递。这一特性导致了reactive在解构时会丢失响应式。
constposition=reactive({x:0,y:0})// 错误操作:相当于执行了 let x = 0;// 此时 x 变成了一个纯粹的数字,脱离了 position 对象的 Proxy 拦截let{x}=position如果使用ref,即使将其作为参数传递给其他子函数,传递的也是该 Ref 对象的引用,.value上的拦截机制依然生效,因此不会丢失响应式。
4. 选型结论
在实际开发中,基于上述底层特性,通常遵循以下选型原则:
- **必须用
ref**:
- 字符串、数字、布尔值等基本类型(例如控制显隐的
isOpen,计数器count)。 - 需要从后端接口获取并整体替换的数组或对象(避免使用
reactive后不得不遍历去push或使用Object.assign)。
- **适用
reactive**:
- 结构固定的复杂对象,且内部属性高度关联(例如标准的表单提交对象
const loginForm = reactive({ username: '', password: '' }))。
