ref 和 reactive 是 Vue 3 响应式通信的底层基础而非直接通信机制,它们为 props、emit、v-model、provide/inject 等提供响应性支持;ref 用于单值追踪,reactive 用于对象/数组的深层响应;二者在父子通信、双向绑定、跨层级注入中协同工作,但需注意替换对象、解构丢失响应性及 .value 访问等关键细节。

Vue 3 的响应式 API(ref 和 reactive)本身不直接负责组件通信,但它们是实现通信的底层基础。真正完成父子、兄弟或跨层级通信的,是 props、emit、v-model、provide/inject、mitt 或 pinia 等机制——而这些机制背后依赖的,正是 ref 和 reactive 提供的响应式能力。
ref 和 reactive 是“数据管道”,不是“通信协议”
你可以把 ref 和 reactive 理解成给数据装上“监听器”的工具:
- ref 让单个值(如字符串、数字、布尔)可被 Vue 追踪变化;
- reactive 让整个对象或数组的所有属性自动具备响应性。
没有它们,父组件传给子组件的 props 就只是静态快照,子组件内部修改不会触发更新;emit 出去的新值也无法被父组件的 ref 变量实时捕获;v-model 绑定的 input 值更不会自动同步到 data 中。
在常见通信场景中,它们怎么配合工作?
父子通信(props + emit)
立即学习“前端免费学习笔记(深入)”;
- 父组件用
ref或reactive定义状态,再通过 props 传给子组件; - 子组件接收 props(只读),但若需修改并通知父组件,就调用
emit('update:xxx', newValue); - 父组件监听该事件,并用
.value = newValue(ref)或直接赋值(reactive 对象属性)更新本地响应式数据。
v-model 双向绑定
-
v-model="title"实际是:model-value="title" @update:model-value="title = $event"的语法糖; - 只要
title是ref('')或reactive({ title: '' })中的响应式字段,就能实时同步。
provide / inject
- 父组件用
provide('user', reactive({ name: 'Alice', age: 25 }))提供响应式对象; - 子组件用
inject('user')拿到的是同一个代理对象,修改user.name会立刻反映在所有注入处; - 若提供的是
ref,比如provide('count', count),子组件拿到后仍需通过count.value访问和修改。
容易踩坑的关键点
不要在通信中直接替换 reactive 对象
- 错误写法:
user = { name: 'Bob' }—— 这会让响应式失效; - 正确做法:逐个赋值
user.name = 'Bob',或用Object.assign(user, { name: 'Bob' })。
解构会丢失响应性,通信时尤其危险
- 从
reactive解构出的变量(如const { name } = user)是普通值,改它不会触发更新; - 跨组件传递时,应传整个 reactive 对象,或用
toRefs包装后再解构。
ref 在模板中自动解包,JS 中必须 .value
- 模板里写
{{ count }}没问题; - 但在 setup 或 script 中写
if (count === 5)是错的,得写if (count.value === 5)。
选 ref 还是 reactive?看通信粒度
用 ref 的典型场景:
- 需要独立暴露、单独监听的单一状态(如 loading、error、searchQuery);
- 要作为参数传给组合式函数(composable),因为 ref 更易被 toRef/toRefs 处理;
- 与第三方库集成时,对方期望接收一个带 .value 的引用对象。
用 reactive 的典型场景:
- 表单数据整体管理(如
form = reactive({ username, password, agree })); - API 返回的嵌套对象,需保持深层响应性且频繁读写属性;
- provide/inject 时希望子组件能自然访问
store.user.name而非store.user.value.name。


















