文章目录
- 一、核心机制对比
- 二、底层实现剖析
- 1. reactive简单实现
- 2. ref 实现原理
- 三、实战场景对比
- 1. 基本类型处理
- 2. 对象类型处理
- 3. 数组处理
- 四、高级特性差异
-
- 五、性能对比分析
- 六、最佳实践
-
- 七、特殊场景处理
-
- 八、总结选择策略
- 1. 优先使用 ref 的场景
- 2. 优先使用 reactive 的场景
- 3. 混合使用
- 3.1 reactive 嵌套 ref
- 3.2 ref 嵌套 reactive
一、核心机制对比
特性 | ref | reactive |
---|
包装对象 | RefImpl类实现 | Proxy代理对象 |
响应式原理 | 通过 .value 的 getter/setter 拦截 | 深度代理对象的属性访问 |
数据类型支持 | 任意类型(推荐基本类型) | 仅对象/数组/集合类型 |
深层响应式 | 自动展开(对象会转为 reactive) | 默认深层响应 |
模板自动解包 | 支持(顶层属性自动解包) | 不支持(需保持对象引用) |
TS类型推断 | Ref 类型包裹 | 原始类型推断 |
二、底层实现剖析
1. reactive简单实现
const reactive = (target) => {return new Proxy(target, {get(target, key) {track(target, key)return Reflect.get(target, key)},set(target, key, value, receiver) {const oldValue = target[key]const result = Reflect.set(target, key, value, receiver)if (oldValue !== value) {trigger(target, key)}return result}})
}
- 依赖收集(Track):
- 通过 track 函数将当前正在执行的副作用(effect)与目标对象的属性关联。
- 使用 WeakMap 存储依赖关系,结构为:
targetMap = WeakMap({target: Map({key: Set(effect1, effect2, ...)})
});
- 触发更新(Trigger):
- 当属性变化时,通过 trigger 函数找到所有关联的副作用并执行。
2. ref 实现原理
class RefImpl {constructor(value) {this._value = isObject(value) ? reactive(value) : value;this.dep = new Set(); }get value() {track(this.dep); return this._value;}set value(newVal) {if (this._value !== newVal) {this._value = isObject(newVal) ? reactive(newVal) : newVal;trigger(this.dep); }}
}const ref = (value) => new RefImpl(value);
- 依赖收集与触发更新:
- 每个 ref 实例内部维护一个 dep 集合(类似 reactive 的依赖管理)。
- 当通过 .value 访问时,触发 track;修改时触发 trigger。
- 关键设计:
- 值类型包装: 通过 .value 访问值,解决基本类型无法被 Proxy 直接代理的问题。
- 自动解包: 在模板中使用 ref 时,Vue 会自动解包(无需写 .value)。
三、实战场景对比
1. 基本类型处理
const count = ref(0)
const count = reactive(0)
2. 对象类型处理
const user = ref({name: 'John',address: {city: 'New York'}
})
const user = reactive({name: 'John',address: reactive({city: 'New York'})
})
console.log(user.value.name)
console.log(user.name)
3. 数组处理
const listRef = ref([1, 2, 3])
listRef.value.push(4)
const listReactive = reactive([1, 2, 3])
listReactive.push(4)
四、高级特性差异
1. 响应式丢失问题
const state = reactive({ count: 0 })
const { count } = state
const countRef = ref(0)
const count = countRef
const state = reactive({ count: 0 })
const countRef = toRef(state, 'count')
2. 类型替换场景
const data = ref({ items: [] })
data.value = fetchData()
const data = reactive({ items: [] })
Object.assign(data, fetchData())
data = fetchData() ❌ 破坏响应性
五、性能对比分析
操作 | ref(基本类型) | reactive(对象) | 差异原因 |
---|
创建速度 | ★★★★☆ | ★★★☆☆ | Proxy初始化成本较高 |
属性访问 | ★★★★☆ | ★★★☆☆ | Proxy拦截带来额外开销 |
深层监听 | ★☆☆☆☆ | ★★★★★ | Proxy自动深度代理 |
内存占用 | 较低 | 较高 | Proxy对象占用更多内存 |
六、最佳实践
1. 组合式函数规范
function useCounter(initial = 0) {const count = ref(initial)return { count }
}
function useUser() {const state = reactive({name: '',age: ''})return { ...toRefs(state) }
}
2. 表单处理策略
const form = reactive({username: '',password: '',preferences: {theme: 'light',notifications: true}
})
const searchQuery = ref('')
七、特殊场景处理
1. DOM 引用
获取元素对象或者组件对象只能使用 ref ,reactive无法处理 DOM 引用
<script setup>javascript">const inputRef = ref(null)
</script><templete><div ref="inputRef"></div>
</templete>
2. 第三方库集成
const chartInstance = ref(null)
const options = reacitve({title: { text: '热销' },series: [ ... ]
})
八、总结选择策略
1. 优先使用 ref 的场景
- 基本类型值(string/number/boolean)
- 需要模板自动解包
- 可能被整体替换的对象
- 需要传递给composable函数的参数
2. 优先使用 reactive 的场景
- 复杂嵌套对象
- 需要深度响应式监听
- 表单/配置对象等结构化数据
- 需要直接操作集合类型(Map/Set)
3. 混合使用
3.1 reactive 嵌套 ref
const state = reactive({count: ref(0), user: ref<User>({ name: '' })
})
- 行为特点: Vue 会自动解包嵌套在 reactive 中的 ref,访问时无需 .value
state.count++
state.user.name = 'john'
- 适用场景
- 混合响应式类型: 当 reactive 对象需要包含基础类型 + 对象类型的混合数据时,用 ref 统一包裹,享受自动解包特性。
- 外部数据注入: 当某个属性需要从外部接收 ref 类型时(如组合式函数返回的 ref),直接将其嵌入 reactive 对象。
3.2 ref 嵌套 reactive
const complexRef = ref({data: reactive({ }), status: 'loading'
})
- 行为特点:
- 层级访问: 需要 .value 访问容器内内容
- 响应式范围:
- complexRef.value.data 是响应式对象(reactive 创建)
- complexRef.value.status 是普通字符串(非响应式)
complexRef.value.data.key = "value";
complexRef.value.status = "success";
- 适用场景:
- 整体替换响应式对象 :如果需要完全替换整个响应式对象,用 ref 包裹可以保持引用。
- 组合非响应式数据: 当需要混合响应式和非响应式数据时,ref 作为容器更灵活。
complexRef.value = { data: reactive({ }),status: 'success'
};