【Vue3】Ref与Reactive的区别

Ref与Reactive的区别

ref与reactive的区别已经是我们面试中的老问题了,一般情况下我们都会通过对象的类型来决定:基本类型使用ref,引用类型使用reactive。但其实ref也能用来创建引用类型的对象,即ObjectArray,那么我们应该在什么情况下使用ref,什么情况下使用reactive呢,接下来就从源码入手,分析一下这两个的区别。

我们都知道,Vue3的双向绑定的实现逻辑已经从defineProperty变成了Proxyrefreactive都会创建一个Proxy对象,接下来就来看看是如何实现的

ref 与 shallowRef

refshallowRef的区别在于是否为深响应式,一般情况下我们都是使用ref,而shallowRef只会对.value的变更进行响应式处理,即使是引用类型也是如此。使用场景可以参考减少大型不可变数据的响应性开销

function ref(value) {
    return createRef(value, false);
}
function shallowRef(value) {
    return createRef(value, true);
}
function createRef(rawValue, shallow) {
    // 如果已经是ref对象,则直接返回,不做任何事情
    if (isRef(rawValue)) {
        return rawValue;
    }
    return new RefImpl(rawValue, shallow);
}

以上是refshallowRef方法的实现,我们可以看到两个方法都是返回了createRef方法,只不过 第二个参数(shallow) 的传参不一样,而createRef方法也是十分的简单:先判断了当前传入的对象是否已经是Ref对象,若是则直接返回,否则返回一个RefImpl对象,接下来看看isRefRefImpl的具体实现:

function isRef(r) {
    return !!(r && r.__v_isRef === true);
}

var RefImpl = class {
    constructor(value, __v_isShallow) {
        // 是否浅ref
        this.__v_isShallow = __v_isShallow;
        this.dep = void 0;
        // 是否为Ref对象
        this.__v_isRef = true;
        this._rawValue = __v_isShallow ? value : toRaw(value);
        // 将值赋值给 _value
        this._value = __v_isShallow ? value : toReactive(value);
    }
    get value() {
        // 依赖收集
        trackRefValue(this);
        return this._value;
    }
    set value(newVal) {
        const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal);
        newVal = useDirectValue ? newVal : toRaw(newVal);
        if (hasChanged(newVal, this._rawValue)) {
            this._rawValue = newVal;
            // 如果新赋予的值是一个对象,那么包装成Proxy
            this._value = useDirectValue ? newVal : toReactive(newVal);
            triggerRefValue(this, 4 /* Dirty */, newVal);
        }
    }
}
// toReactive首先判断value是否为对象,若是对象则进行包装,否则直接返回
var toReactive = (value) => isObject(value) ? reactive(value) : value;

我们可以看到在新建RefImpl类的时候通过this.__v_isRef = true来定义是否为 Ref 对象,isRef方法也是基于这个变量来返回。

RefImpl类中,可以知道值是存在_value中的,而get value也是返回了_value的值,因此我们在对Ref对象进行操作的时候需要通过.value的方式进行修改,而不能直接进行赋值。

_value的定义我们可以发现,当不是shallow的时候,将value通过toReactive方法转变成了reactive。由此知晓:ref 的 .value 与 reactive 其实是同一个东西,接下来就来看看 reactive 到底是个啥

reactive 与 shallowReactive

reactiveshallowReactive 的区别同 ref

function reactive(target) {
    if (isReadonly(target)) {
        return target;
    }
    return createReactiveObject(
        target,
        false,
        mutableHandlers,
        mutableCollectionHandlers,
        reactiveMap
    );
}
function shallowReactive(target) {
    return createReactiveObject(
        target,
        false,
        shallowReactiveHandlers,
        shallowCollectionHandlers,
        shallowReactiveMap
    );
}

我们可以发现跟ref一样,他们都返回了一个createReactiveObject

function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {
   // 判断是否为对象,若不是则抛出异常
   if (!isObject(target)) {
       if (true) {
           warn(`value cannot be made reactive: ${String(target)}`);
       }
       return target;
   }
   if (target["__v_raw" /* RAW */] && !(isReadonly2 && target["__v_isReactive" /* IS_REACTIVE */])) {
       return target;
   }
   const existingProxy = proxyMap.get(target);
   if (existingProxy) {
       return existingProxy;
   }
   const targetType = getTargetType(target);
   if (targetType === 0 /* INVALID */) {
       return target;
   }
   const proxy = new Proxy(
       target,
       targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers
   );
   proxyMap.set(target, proxy);
   return proxy;
}

到了这一步就已经得知,refreactive返回了Proxy的原理。

总结

基本类型的数据使用ref,引用类型根据赋值方式来决定使用ref还是reactive

如果是要直接替换整个对象,也就是赋值的方式,就使用ref

如果是要修改对象的属性,就使用reactive

因为ref.value替换对象仍能保持响应式,而reactive不行

ref

  1. ref的变量必须使用.value赋值,不然会把ref变成普通的数据,失去响应式。
  2. ref的值如果是对象,里面的对象是响应式的,因为引用类型会先用Proxy包装再赋值。
  3. 如果是shallowRef则对象不会用Proxy包装。
  4. ref(reactive({a:123}))等价于ref({a:123}),若在 shallowRef 中使用 reactiveshallowRef 会失去作用

reactive

  1. 无需.value即可修改对象的值且具备响应式
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋丶海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值