一、核心区别
1. 数据类型与使用场景
• ref
可定义基本类型(字符串、数字、布尔值)和对象类型的响应式数据。对于对象类型,`ref` 内部会自动调用 `reactive` 将其转换为响应式对象。
语法特点:需通过 `.value` 访问或修改数据(模板中自动解包,无需 `.value`)。
适用场景:简单数据、需跨组件传递的独立变量、需要重新赋值的场景(如替换整个对象)。
• reactive
仅支持对象类型(对象、数组、Map/Set 等),通过 `Proxy` 实现深度响应式代理。
语法特点:直接访问属性(如 `state.count`),无需 `.value`,但无法直接替换整个对象(需用 `Object.assign` 合并更新)。
适用场景:复杂嵌套对象、需深度响应式追踪的复杂数据结构。
2. 响应式机制差异
ref 底层原理
通过封装对象的 `.value` 属性实现响应式:
◦ 对基本类型使用 `Object.defineProperty` 的 `get/set` 进行数据劫持。
◦ 对对象类型内部调用 `reactive` 转换为 `Proxy` 代理。
```javascript
// 简化的 ref 实现逻辑
function ref(value) {
return {
get value() { track(this, 'value'); return value; },
set value(newVal) { value = newVal; trigger(this, 'value'); }
};
}
```
reactive 底层原理
基于 `Proxy` 拦截对象属性的增删改查,结合 `Reflect` 操作原始数据:
```javascript
// 简化的 reactive 实现逻辑
function reactive(obj) {
return new Proxy(obj, {
get(target, key) { track(target, key); return Reflect.get(target, key); },
set(target, key, value) {
Reflect.set(target, key, value);
trigger(target, key);
return true;
}
});
}
```
所有嵌套属性均会被递归代理,实现深层响应性。
二、关键特性对比
特性 | ref | reactive |
---|---|---|
数据类型 | 基本类型 + 对象类型 | 仅对象类型 |
访问方式 | 需 .value (模板自动解包) | 直接访问属性(如 state.key ) |
重新赋值 | 支持(通过 .value = ) | 需合并更新(如 Object.assign ) |
解构响应性 | 解构后仍需 .value | 解构会丢失响应性,需 toRefs |
性能 | 基本类型更轻量 | 复杂对象更高效(Proxy 深度监听) |
三、设计理念与使用建议
1. 设计哲学
• ref
提供单一值响应式的原子化封装,适合组件间传递独立状态。
• reactive
针对复杂状态树设计,通过 Proxy
实现细粒度依赖追踪,优化深层更新性能。
2. 使用建议
• 优先 ref
的场景:
◦ 简单数据(如计数器、表单字段)。
◦ 需要频繁替换整个对象(如接口返回数据更新)。
• 优先 reactive
的场景:
◦ 复杂配置对象(如含多层嵌套的表单数据)。
◦ 需要自动追踪属性增删的场景(如动态表单字段)。
3. 注意事项
• reactive
直接替换整个对象会丢失响应性,需用 Object.assign
合并更新。
• 模板中 ref
对象自动解包,但 JavaScript 中必须使用 .value
。
• 使用 toRefs
解构 reactive
对象可保持响应性。
四、总结
ref
和 reactive
是 Vue3 响应式系统的两大核心 API:
• ref
通过 .value
封装简化基本类型响应式,兼顾对象类型的灵活性。
• reactive
利用 Proxy
实现深度监听,适合复杂状态管理。
开发者应根据数据类型、更新频率及使用场景选择最合适的 API,必要时结合 toRefs
优化代码结构。
异步输出
async await后面的任务是会加入微任务队列,本身是同步函数的话直接就console.log()
this指向输出
const定义变量时不会被挂载到window上
ref
和 reactive
是 Vue 3 中构建响应式数据的两大核心 API。虽然它们在使用上相似,但底层机制和适用场景有明显不同。
🧠 一句话理解
ref()
:用于包装基本类型(也可包装对象),返回一个响应式对象,值通过.value
访问。reactive()
:用于将对象转换为响应式对象,是基于Proxy
实现的深层响应式代理。
⚙️ 底层机制对比
特性 | ref | reactive |
---|---|---|
适用数据类型 | 基本类型 / 对象 | 只能用于对象(数组、Map、Set、纯对象) |
响应式实现方式 | 使用 Object.defineProperty 包装 .value 属性(浅层响应) | 使用 Proxy 对原对象进行深层代理 |
访问方式 | 通过 .value 访问/赋值 | 直接使用对象属性 |
是否深度响应 | 否(但 ref({}) 内部值可递归为 reactive ) | 是(默认深度响应) |
是否可自动解包 | 是(在 <script setup> 和模板 中) | 否(属性不需要 .value ,但不能自动解包嵌套 ref ) |
🔍 底层原理简要(源码角度)
✅ ref
原理(简化示意):
function ref(value) {
return {
__v_isRef: true,
get value() {
track() // 依赖收集
return value
},
set value(newVal) {
value = newVal
trigger() // 响应式触发
}
}
}
ref 会为
value
属性设置 getter/setter,用于追踪依赖和更新响应。
✅ reactive
原理(简化示意):
function reactive(target) {
return new Proxy(target, {
get(obj, key) {
track(obj, key) // 依赖收集
return Reflect.get(obj, key)
},
set(obj, key, value) {
const result = Reflect.set(obj, key, value)
trigger(obj, key) // 触发更新
return result
}
})
}
reactive 会使用
Proxy
监听整个对象,对所有属性深层响应。
✅ 什么时候用 ref
,什么时候用 reactive
场景 | 使用方式 |
---|---|
管理一个单一的基本类型变量(如 count ) | ref(0) |
管理一个对象或数组 | reactive({}) |
需要手动 .value 引用 | 使用 ref |
结构清晰、字段多的响应式对象 | reactive 更方便 |
🧪 示例对比
const count = ref(0)
count.value++ // ✅
const state = reactive({ count: 0 })
state.count++ // ✅
也可以这样混用:
const state = reactive({
count: ref(0) // 嵌套 ref
})
state.count.value++
🧬 组合推荐
在实际项目中,Vue 推荐你:
- 对 基础数据用
ref
- 对 结构性数据用
reactive
- 如有嵌套响应性需求,可以混合使用
如果你想深入源码理解 ref
和 reactive
在响应式系统中的角色,我可以进一步讲解 Vue 3 中的 effect.ts
、reactivity.ts
模块。是否需要?