reactive() vs ref() 深度解析
核心准则
"仅在高度聚合的表单对象(>5 个关联字段,且不涉及解构场景)时使用
reactive()"
一句话总结:ref() 是安全的默认选择,reactive() 是特定场景下的便利性优化。
一、选择决策树
开始
├─ 字段数 > 5 且 不会解构?
│ └─ 是 → reactive() ← 省去大量 .value,提升代码简洁度
│ └─ 否 ─┐
├─ 字段数 ≤ 5?
│ └─ 是 → ref() ← 几个 .value 不费事,更安全
├─ 涉及解构?
│ └─ 是 → ref() ← reactive 解构会丢失响应式连接
├─ 需要包裹原始值(string/number/boolean)?
│ └─ 是 → ref() ← reactive() 不支持原始值
└─ 默认 → ref()
二、特性对比
| 维度 | ref() |
reactive() |
|---|---|---|
| 读取/赋值 | 需加 .value(script 中) |
直接操作属性 |
| 模板中使用 | 自动 unwrap,无需 .value |
直接使用 |
| 响应式丢失风险 | 解构/赋值不会丢失 | 解构或赋值极易丢失(需 toRefs 补救) |
| 支持原始值 | 支持(string/number/boolean 等) | 不支持(只能包对象) |
| TypeScript 类型推断 | 泛型支持良好,推断稳固 | 嵌套类型有时推断不准确 |
| 包裹对象写法 | ref({...}) → form.value.name |
reactive({...}) → form.name |
三、reactive() 的三个使用门槛
1. 高度聚合
字段在逻辑上属于同一个实体,总是一起被操作。
适合 reactive: 复杂订单提交表单(name, price, quantity, address, phone, remark)
- 增删改查都针对这个整体
- 很少单独修改其中一个而不考虑其他
2. 字段数 > 5
阈值是为了平衡"写法便利性"和"响应式丢失风险"。
为什么是 5?
- 字段很少(2-3 个):多写几个
.value不费事,反而更安全 - 字段很多(>5):如果反复读写 5+ 个字段,到处写
.value让代码臃肿难读 - 此时
reactive()能显著提升代码简洁度
3. 不涉及解构
这条是最关键的限定条件。
// ❌ 错误:解构 reactive —— username 丢失响应式
const form = reactive({ username: 'alice', password: '123' })
const { username, password } = form
// 修改 username 不会触发 UI 更新!
// ✅ 正确方案一:用 ref 包裹
const form = ref({ username: 'alice', password: '123' })
const { username, password } = form.value // 解构后仍是普通值,但改了父对象不影响
// ✅ 正确方案二:用 toRefs 保持响应式
const form = reactive({ username: 'alice', password: '123' })
const { username, password } = toRefs(form)
// username.value = 'bob' ← 这样能触发更新,但写法又回去了
解构出的变量本身是普通值,不触发更新。如果修改原始 reactive 对象上的属性(form.username = 'new'),响应式依然在,但解构后几乎没人会这么写。
四、实战场景对比
场景 A:推荐用 reactive
// 7 个字段,整体操作,不解构 → reactive 合适
const orderForm = reactive({
name: '',
price: 0,
quantity: 1,
address: '',
phone: '',
remark: '',
express: '',
})
function submitOrder() {
// 省去 7 个 .value,直接批量赋值
orderForm.price = calcPrice()
orderForm.quantity = 2
// ...
}
场景 B:推荐用 ref
// 2 个字段 → ref 更好
const account = ref('')
const pwd = ref('')
// 一目了然,且随时可以安全地解构传给子组件
场景 C:折中方案 — ref 包裹整个对象
// 字段多但又想更安全 → ref({...})
const form = ref({
name: '',
pwd: '',
email: '',
age: 0,
phone: '',
})
// 函数顶部一次解包,避免到处写 .value
function handleSubmit() {
const data = form.value
// 批量操作 data.name、data.pwd……
}
// 模板中自动 unwrap,form.name 无需 .value
五、记忆口诀
字段少、要解构,就用 ref 别回头;
字段多、不解构,用 reactive 更顺手。
六、进阶:shallowRef / shallowReactive
| API | 层级 | 适用场景 |
|---|---|---|
ref() |
深层响应式 | 默认选择 |
shallowRef() |
仅 .value 变更触发更新 |
大数据列表优化 |
reactive() |
深层响应式 | 条件满足时使用 |
shallowReactive() |
仅第一层属性响应式 | 性能敏感且结构已知 |

浙公网安备 33010602011771号