Vue
ref 与 reactive
一、区别
// ref:包一层 { value: xxx },任意类型都能接
const count = ref(0)
const user = ref({ name: 'Tom' })
console.log(count.value) // 要 .value
// reactive:直接代理对象,只能接对象 / 数组
const state = reactive({ name: 'Tom' })
console.log(state.name) // 不用 .value| ref | reactive | |
|---|---|---|
| 接收类型 | 任意 | 只能对象 / 数组 |
| 访问方式 | .value | 直接访问 |
| 整体替换 | count.value = 新值 ✅ | state = 新对象 ❌ 丢失响应式 |
| 模板中 | 自动解包,无需 .value | 直接用 |
二、ref 传入对象时,内部是 reactive
是的。 ref 内部会调用 toReactive(),对象类型直接走 reactive()。
// 源码简化
function ref(rawValue) {
return {
get value() {
track(this, 'value')
return this._value
},
set value(newVal) {
this._rawValue = newVal
this._value = toReactive(newVal) // 关键在这里
trigger(this, 'value')
},
}
}
function toReactive(value) {
return isObject(value) ? reactive(value) : value
}所以:
const data = ref({ nested: { a: 1 } })
// data → ref 对象(有 .value)
// data.value → reactive 代理对象
// data.value.nested → 也是 reactive(Proxy 的 get 惰性递归代理)
data.value.nested.a = 2 // ✅ 触发更新验证:
import { ref, isReactive } from 'vue'
const data = ref({ nested: { a: 1 } })
isReactive(data) // false,ref 本身不是 reactive
isReactive(data.value) // true,被 reactive() 包了
isReactive(data.value.nested) // true,Proxy 惰性递归代理三、完整流程
ref({ nested: { a: 1 } })
│
├─ 创建 ref 对象 { _value, get value(), set value() }
│
├─ 传入的是对象 → toReactive() → reactive({ nested: { a: 1 } })
│ │
│ └─ 返回 Proxy
│
└─ 访问 data.value.nested 时
→ Proxy get 拦截
→ nested 也是对象 → 惰性调用 reactive({ a: 1 })
(不是初始化时递归,是访问时才代理)四、什么时候用 ref,什么时候用 reactive
// 基本类型 → 只能 ref
const count = ref(0)
// 对象且不需要整体替换 → reactive 更方便(不用 .value)
const form = reactive({ name: '', age: 0 })
// 对象但需要整体替换 → ref
const user = ref({ name: 'Tom' })
user.value = { name: 'Jerry' } // ✅ ref 可以整体替换
// reactive 整体替换会丢失响应式五、一句话总结
ref传入对象时,内部调用toReactive(),本质就是reactive()包一层。所以ref({})的.value和深层属性都是 Proxy 代理的响应式对象,ref只是多了.value的包装,让基本类型也能响应式。