Vue3 ref与reactive区别及实现原理
说明Vue3中ref和reactive的区别、各自的使用场景,以及ref在模板中自动解包(unwrapping)的规则。它们底层是如何通过Proxy实现的?
回答
编译有声
ref vs reactive 核心区别
| 特性 | ref | reactive |
|---|---|---|
| 数据类型 | 任意值(基本类型+对象) | 仅对象(object/array/Map/Set) |
| 访问方式 | .value属性 | 直接访问 |
| 模板解包 | 自动解包(顶层ref) | 不自动解包 |
| 响应式原理 | 包装为RefImpl类 | 直接Proxy包装 |
ref实现原理
function ref(value) {
return new RefImpl(value);
}
class RefImpl {
constructor(value) {
this.__v_isRef = true;
this._value = toReactive(value); // 对象则转reactive
}
get value() {
track(this, 'value'); // 收集依赖
return this._value;
}
set value(newVal) {
this._value = toReactive(newVal);
trigger(this, 'value'); // 触发更新
}
}
reactive实现原理
function reactive(target) {
if (target && typeof target === 'object') {
return new Proxy(target, handlers);
}
return target;
}
模板自动解包规则
在<template>中,顶层ref自动解包(无需.value):
<script setup>
const count = ref(0);
const obj = reactive({ num: ref(10) });
</script>
<template>
<p>{{ count }}</p> <!-- ✅ 自动解包,显示0 -->
<p>{{ obj.num }}</p> <!-- ❌ 不解包,显示 RefImpl -->
<p>{{ obj.num.value }}</p> <!-- ✅ 需手动.value -->
</template>
ref声明对象时:ref(plainObject)内部自动调用reactive,所以arr.value[0]直接响应式。
使用建议:
- 基本类型(string/number/boolean):必须用ref
- 复杂对象(form、API响应):reactive更简洁
- 需要重新赋值整个对象:ref更方便(
obj.value = newObjvs reactive需要替换属性) - 跨组件传递/需解构:ref配合toRefs
- 模板中使用:组合式设ref,纯数据对象设reactive