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() 仅第一层属性响应式 性能敏感且结构已知

七、参考链接

posted @ 2026-07-01 10:53  HuangBingQuan  阅读(17)  评论(0)    收藏  举报