
你写 Vue 3 组合式 API,是不是在 ref 和 reactive 之间纠结:到底该用哪个?改了数据视图却不刷新,八成是用错了。Vue 3 的 ref 和 reactive 区别,核心在“ref 包任意类型、靠 .value 访问;reactive 只包对象、直接访问”。本文用最小示例讲清三个要点、怎么选型,以及两个最容易踩的坑。看完你能用对两者。今天这篇文章,编程狮就把这块讲透。三个要点从 .value 到整体替换,再到解构丢失响应,把最容易让视图不刷新的坑一次点透。
一、先看结论
| 你的数据 | 该用 | 访问方式 |
|---|---|---|
| 基本类型(数字/字符串/布尔) | ref |
在 JS 里用 .value,模板里不用 |
| 对象 / 数组等复合结构 | reactive 或 ref 都行 |
reactive 直接取;ref 包对象也用 .value |
| 需要整体替换对象 | ref |
ref 能整个重新赋值,reactive 不能 |

二、响应式到底怎么生效
Vue 3 ref reactive 区别 的根源,是 Vue 用 Proxy 把数据变成“响应式”:数据变了他帮你刷新视图。但 Proxy 只能代理“对象”,所以基本类型(数字、字符串)没法直接被代理。ref 的解决办法是把基本类型包进一个对象 { value: x },再对这个值做响应式;reactive 则直接对整个对象套 Proxy。理解这点,两者的差异就顺了。
一个比喻:reactive 像给“整栋房子”装监控(房子=对象),里面每个房间变化都能看见;ref 像给“一件单品”套个带编号的盒子(.value 就是编号),无论单品还是整盒都能被追踪。建议先过一遍 Vue3 教程 的响应式章节。
三、要点一:ref 包基本类型,访问要 .value
import { ref } from 'vue';
// ref 包装一个数字
const count = ref(0);
console.log(count.value); // 在 JS 里必须加 .value,输出 0
count.value++; // 修改也要通过 .value
// 模板里不用 .value,Vue 自动解包
// <template> {{ count }} </template>
上面这段做了什么:用 ref(0) 把数字 0 包成响应式对象,count.value 才是真正的数值。在 <script> 里读写都要带 .value;但在模板里 Vue 会自动解包,直接写 {{ count }} 即可。漏写 .value 是最常见的“拿到对象而不是值”的错误。
四、要点二与三:reactive 直接访问,ref 可整体替换
import { reactive, ref } from 'vue';
// 要点二:reactive 直接访问属性,无需 .value
const user = reactive({ name: '编程狮', age: 3 });
console.log(user.name); // 直接取,输出 编程狮
user.age++;
// 要点三:ref 能整体重新赋值,reactive 不行
const list = ref([1, 2, 3]);
list.value = [4, 5, 6]; // 合法:整个替换
const obj = reactive({ a: 1 });
// obj = { a: 2 }; // 错误:reactive 不能整体重赋值,会丢失响应式
Object.assign(obj, { a: 2 }); // 正确:原地改属性
上面这段做了什么:要点二里 reactive 返回的对象能直接 .name 访问,体验更自然。要点三很关键:如果你后面需要“整个换成新对象”(比如接口返回新数据覆盖旧数据),必须用 ref,因为 reactive 绑定的变量本身不能重赋值,重赋值会断掉响应式;想改 reactive 只能 Object.assign 原地更新。
五、两个最容易踩的坑
| 现象 | 原因 | 修复 |
|---|---|---|
| JS 里取到的不是值,是对象 | ref 忘了写 .value |
访问/修改 ref 都加 .value |
| reactive 整体赋值后视图不更新 | 重赋值断开了响应式绑定 | 用 ref 包对象,或 Object.assign 原地改 |
第一个坑:新手常在 ref 上直接 count++ 而不是 count.value++,结果改的是包装对象而非内部值,视图不刷新。记住“脚本里见 ref 就加 .value”。第二个坑:把 reactive 对象整个重新赋值(obj = newObj),变量已指向新引用,Vue 追踪的旧代理失效,视图卡住。要覆盖就用 ref 包对象后 x.value = newObj,或保持 reactive 用 Object.assign 原地合并。
Vue3 的组合式 API 里,ref 和 reactive 都是让数据“变响应式”的工具,但适用对象和用法完全不同,选错就会踩坑。ref 用来包装基本类型(数字、字符串、布尔),因为它必须是一个“对象”才能拦截读写,所以访问时要写 .value——模板里 Vue 会自动拆包所以不用写,但在 <script> 里一定要写。reactive 只能用于对象或数组,直接 reactive({...}) 后得到一个深层响应式的代理,访问属性不用 .value。一句话选型:基本类型用 ref,对象用 reactive。
最容易翻车的几个点。其一,对 reactive 对象做“整体替换”会丢失响应性:state = reactive({a:1}); state = {a:2} 这样重新赋值后,新对象不再是响应式的;要改就用 Object.assign 或逐属性改,或者干脆把对象包进 ref 用 ref({...}) 再整体替换。其二,把 reactive 对象或 ref 解构出来会丢响应性:const { a } = state 拿到的是普通值;要解构又保留响应,用 toRefs(state),它把每个属性变成独立的 ref。其三,ref 嵌套在 reactive 里时,模板访问不用叠 .value,Vue 会层层自动拆包,别多写。
还有些实用细节。isRef 可以判断一个值是不是 ref,unref(x) 等价于 isRef(x) ? x.value : x,在写通用函数时很方便;shallowRef / shallowReactive 只在顶层做响应、不深层代理,适合管理大对象或需要手动控制更新频率的场景。选型上给个速查:单个基本类型 → ref;一组成组的状态对象 → reactive 配 toRefs;需要整体替换的复杂状态 → 用 ref 包对象。把这些边界记清楚,组合式 API 的状态管理就不会再让你半夜 debug。
落到真实组件里:表单用 ref 包每个字段最直观;从接口拿回一整份数据要覆盖旧状态时,用 ref 包对象再 x.value = res,或保持 reactive 用 Object.assign 原地合并。模板里 ref 自动解包,但 ref 作为 reactive 的属性或出现在 v-for 中时仍可能要手动处理,遇到拿不到值先想是不是漏了 .value。把“基本类型用 ref、对象可 reactive、见 ref 就点 .value”记牢,响应式就不会再半夜找你。
动手配置好 Vue3 项目,把 ref/reactive 示例运行,检查视图是否随数据刷新;若视图不更新,按“漏写 .value”的方向排查失败原因并修复。适用场景、性能取舍与边界写法下面根据实际运行与官方文档整理。
总结
要点带走:
- ref 包基本类型、访问改值都要
.value;模板里自动解包; - reactive 只包对象、直接访问属性,语义更自然;
- 需要“整体替换数据”用 ref 再
.value = 新值,reactive 只能Object.assign原地改; - 一个组件里不必二选一僵化:基本类型用 ref,复杂对象两者皆可,关键是别漏
.value。
下一步建议系统过一遍 Vue3 教程,把Vue 和 React 怎么选 和响应式原理一起学。
延伸学习
想把这块知识系统补齐,可以按这个顺序来:
Q:ref 和 reactive 到底先用哪个?
A:简单记:基本类型(数字、字符串、布尔)只能用 ref;对象/数组两者都行。如果你习惯统一写法、或后面要整体替换数据,建议一律用 ref,避免 reactive 不能重赋值的坑。
Q:为什么 ref 在模板里不用写 .value?
A:Vue 在模板渲染时会自动对 ref 解包,所以 {{ count }} 直接拿到内部值。但注意:只有当 ref 是顶层属性时才会自动解包,ref 作为 reactive 对象的属性或 v-for 里等情况,仍可能要手动处理。
Q:reactive 真的不能整体重赋值吗?
A:对。reactive 返回的是 黑神话前端架构 代理,变量被重新赋值后会指向新引用,旧代理的响应式追踪也就断了,视图不再更新。要覆盖数据请改用 ref(.value = 新对象),或保持 reactive 用 Object.assign(obj, 新数据) 原地更新。

TRAE-AI编程



