锦书在线
80.52M · 2026-03-21
在 Vue 3 的 Composition API 中,ref 和 reactive 是定义响应式数据的两大基石。很多初学者常纠结于“什么时候该用哪个”。本文将从底层原理到实战场景,带你彻底理清两者的区别。
定义:主要用于定义基本类型(String, Number, Boolean 等),也可以定义引用类型。
本质:通过对原始值进行包装,生成一个具有 .value 属性的对象。对于引用类型,ref 内部会自动调用 reactive 来处理。
访问控制:
.value 访问;<template> 模板中,Vue 会自动解包,直接写变量名即可,无需加 .value。定义:专门用于定义引用类型(Object, Array, Map, Set)。
本质:基于 ES6 Proxy 实现,直接代理整个对象。
访问控制:像操作普通原生对象一样直接访问属性,无需 .value。
| 特性 | ref | reactive |
|---|---|---|
| 支持类型 | 基本类型 + 引用类型 | 仅限 引用类型 |
| JS 访问方式 | 需 .value | 直接访问属性 |
| 模板访问 | 自动解包,无需 .value | 直接访问 |
| 底层实现 | 包装基本类型,内部调用 reactive 处理引用类型 | 基于 Proxy 深度代理整个对象 |
| 替换整个对象 | 支持 (ref.value = 新对象/新数组) | 不支持(直接赋值会丢失代理,失去响应式) |
| 解构支持 | 直接解构丢失响应式(需 toRefs) | 直接解构丢失响应式(需 toRefs) |
ref 的场景:基本类型数据:计数器、开关状态、输入框的值。
需要重置的数据:例如从后端获取列表后,直接 list.value = res.data。
简单组件逻辑:代码更清晰,.value 提醒这是一个响应式变量。
reactive 的场景:复杂业务模型:包含多个相互关联属性的大对象(如用户信息、表单整组数据)。
追求原生感:不希望在逻辑代码中到处看到 .value。
聚合数据:将一类变量聚合在一个对象中管理,减少变量声明。
let state = reactive({ count: 0 });
// 错误操作:这会导致 state 失去响应式,因为它变成了一个普通的普通对象
state = { count: 1 };
// 正确方案 A (ref):
const state = ref({ count: 0 });
state.value = { count: 1 };
// 正确方案 B (Object.assign):
Object.assign(state, { count: 1 });
当你需要从一个响应式对象中提取属性并保持响应式时,必须使用 toRefs,否则会丢失响应式。
const props = reactive({ title: 'Vue3', author: 'Gemini' });
// 直接解构:const { title } = props; -> title 只是一个普通的字符串
const { title } = toRefs(props); // -> title 变成了一个 ref,保持响应式
ref:默认只 .value 的变化,如果 ref 包裹的是对象,深度需要开启 { deep: true }。
reactive:默认强制开启深度,且无法关闭。
.value,但胜在灵活且不易出错。