为什么重新赋值不能用reactive要使用ref

📅 2026/7/1 14:08:38
为什么重新赋值不能用reactive要使用ref
这个问题的核心原因在于JavaScript 的引用传递机制和Vue 的响应式代理Proxy原理。简单来说reactive给你的是一个“代理对象”如果你把整个变量重新赋值就等于把这个代理丢了而ref给你的是一个“装数据的盒子”你重新赋值时修改的是盒子里的内容.value盒子本身没变。1. 为什么reactive重新赋值会失效reactive返回的是一个Proxy 代理对象。你的变量指向的是这个代理。当你进行重新赋值时变量指向了一个全新的普通对象Vue 的代理丢失自然无法追踪变化。import{reactive}fromvue// 1. 初始创建state 指向一个 Proxy 代理letstatereactive({count:1})// 2. 修改属性没问题触发了 Proxy 的 setterstate.count2// ✅ 页面会更新// 3. 重新赋值state 不再指向 Proxy而是指向了一个全新的普通对象 {}state{count:3}// ❌ 致命错误响应式链接彻底断开了// 此时再修改属性Vue 完全感知不到state.count4// ❌ 页面不会更新因为 state 已不是响应式对象2. 为什么ref重新赋值就可以ref返回的是一个RefImpl 实例对象即带有.value属性的盒子。你重新赋值时修改的是盒子里的.value属性盒子本身变量指向始终是那个 Ref 实例从未改变因此 Vue 能够一直监听到.value的变化。import{ref}fromvue// 1. 初始创建data 指向一个 Ref 盒子constdataref({count:1})// 2. 修改属性通过 .value 修改触发了 ref 的 setterdata.value.count2// ✅ 页面会更新// 3. 重新赋值整个对象注意我们修改的是 data.value盒子里的内容data.value{count:3}// ✅ 完美响应式盒子还是那个盒子// 此时修改属性Vue 依然能感知到data.value.count4// ✅ 页面会更新3. 特别注意如果重新赋值整个ref变量会怎样如果你这样写letdataref({count:1})dataref({count:2})// 重新赋值整个变量不是改 .value语法可行data确实变成了一个新的 Ref 盒子它本身依然是响应式的。严重隐患如果模板或watch监听了旧的data变量它们绑定的还是旧盒子的引用新盒子无法被追踪到。这在 Vue 3 的setup中极易引起难以排查的 Bug。Vue 官方最佳实践声明ref时必须使用const永远不要重新赋值变量本身只修改.value。这是写 Vue 3 的铁律。// ❌ 错误示范极其罕见但会踩坑letnumref(0)numref(10)// 虽然新值是响应式但可能造成监听失效// ✅ 绝对正确的写法constnumref(0)num.value10// 改值永远走 .value4. 什么时候该用reactive什么时候用ref对比维度reactiveref适用数据类型对象Object和数组Array所有类型包括基本类型和对象重新赋值整个对象❌绝对不行会丢失响应式✅可以通过.value newObj访问方式直接访问属性state.count必须通过.valuedata.value.count解构/展开❌ 解构后会丢失响应式✅ 配合toRefs可解构典型应用场景表单数据form、配置项config数据结构和属性固定不需要整体替换。任何需要整体替换的数据如接口返回的新对象、基本类型数字、字符串、布尔、异步获取的数据。5. 实战场景为什么你在项目中更容易遇到这个问题最常见的情况是从后端接口获取数据并赋值。// ❌ 错误写法reactiveconstuserInforeactive({name:,age:0})constfetchDataasync(){constresawaitapi.getUser()userInfores.data// 试图整体替换——失效}// ✅ 正确写法refconstuserInforef({name:,age:0})constfetchDataasync(){constresawaitapi.getUser()userInfo.valueres.data// 整体替换——完美生效}总结一句口诀reactive只能改属性obj.key newVal不能换对象obj newObj。ref既能改属性ref.value.key newVal也能换对象ref.value newObj。所以当你的数据未来大概率需要整体替换比如接口返回的新对象时请毫不犹豫地使用ref.value。如果数据结构固定且不需要整体替换如表单校验对象用reactive会更省代码不用到处写.value。