ref用于创建响应式数据,原始值需.value访问,模板自动解包;对象类型模板中可直接访问属性但内部非响应,需用reactive或嵌套ref确保响应性。

ref 是 Vue 3 响应式系统中最基础、最常用的 API 之一,它能让你把任意值(原始值或对象)变成响应式数据。但很多人在使用时容易忽略一个关键细节:Vue 会为 ref 包裹的对象类型自动“解包”(unwrapping),而对原始值则不会——这个行为直接影响你在模板和 JS 中的取值方式。
ref 定义原始值:必须用 .value 访问
当你用 ref 包裹字符串、数字、布尔值等原始类型时,它始终是一个带 .value 属性的响应式对象:
import { ref } from 'vue'<br>const count = ref(0)<br>const name = ref('Alice')<br><br>// JS 中读写必须加 .value<br>console.log(count.value) // 0<br>count.value++ // ✅ 正确<br>// count++ ❌ 错误:count 是个对象,不是原始值在模板中 Vue 会自动帮你解包,所以不用写 .value:
<template><br><div>{{ count }}</div> <!-- 显示 1,自动解包 --><br><button>+1</button><br></template>ref 包裹对象:模板中自动解包,JS 中仍需 .value(但有例外)
当 ref 的值是一个普通对象(包括数组、Date、Map 等非响应式对象)时:
- 在 JavaScript 逻辑中,你依然要通过
.value访问和修改整个对象; - 但在 模板中,Vue 会自动解包一层,允许你直接访问对象属性(如
user.name),就像它是响应式对象一样; - ⚠️ 注意:这种“解包”仅限于模板渲染,且只解一层 —— 不会递归把对象内部也变成响应式(除非该对象本身是 reactive 创建的)。
const user = ref({ name: 'Bob', age: 25 })<br><br>// JS 中:<br>console.log(user.value.name) // ✅<br>user.value = { name: 'Tom', age: 30 } // ✅ 替换整个对象<br>user.value.age++ // ✅ 修改属性(但 age 本身不响应!)<br><br>// 模板中:<br><div>{{ user.name }}</div> <!-- ✅ 自动解包,等价于 user.value.name --><br><div>{{ user.age }}</div>ref 对象属性响应性陷阱:原始值属性不会自动响应
上面例子中 user.value.age++ 能运行,但视图不会更新 —— 因为 { name, age } 是普通对象,它的属性不具备响应性。要让 age 变化触发更新,有两个选择:
- 用
reactive替代:适合结构稳定、属性多的对象; - 把属性也用
ref包裹(较少见,适合动态键或需要单独控制响应性的场景); - 更推荐:用
ref包裹整个对象,但内部属性改用ref或配合reactive。
// ✅ 推荐:用 reactive 管理对象内部响应性<br>const user = reactive({ name: 'Bob', age: ref(25) })<br>// 或更简洁:<br>const user = reactive({ name: 'Bob', age: 25 }) // reactive 会深层代理<br><br>// ✅ 模板中仍可直接写 {{ user.age }},且变化会响应如何判断该用 ref 还是 reactive?
简单口诀:
- 变量是 单个值(数字、字符串、布尔、Promise、组件实例等)→ 用 ref;
- 变量是 结构化数据对象(用户信息、表单、配置项等),且属性会频繁读写 → 优先用 reactive;
- 需要把对象作为 prop 传给子组件,或要在组合式函数中暴露响应式对象 → ref 更灵活(因为 ref 可以被解构、重命名、传递而不失响应性)。
实际项目中,常见混合用法:顶层状态用 ref,内部复杂对象用 reactive,再用 ref 包一层对外暴露。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











