解析Vue3源码(一)——reactive

解析Vue3源码(一)——reactive

前言

不同于Vue2,Vue3声明响应式数据不是在$options.data()中声明而是需手动创建。区别如下

vue2
data: {
	obj: {}
}
vue3 
let obj = reactive({});

reactive的源码

function reactive(target) {
    // 不监听只读数据,直接返回
    if (isReadonly(target)) {
        return target;
    }
    // 返回代理对象
    return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
}
// Proxy的handler
const mutableHandlers = {
    get,
    set,
    deleteProperty,
    has,
    ownKeys
}
//
const get = /*#__PURE__*/ createGetter();
//
const set = /*#__PURE__*/ createSetter();
const mutableCollectionHandlers = {
    get: /*#__PURE__*/ createInstrumentationGetter(false, false)
};

function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers, proxyMap) {
	//非对象类型的数据直接返回
    if (!isObject(target)) {
        if ((process.env.NODE_ENV !== 'production')) {
            console.warn(`value cannot be made reactive: ${String(target)}`);
        }
        return target;
    }
    // target is already a Proxy, return it.
    // exception: calling readonly() on a reactive object
    if (target["__v_raw" /* RAW */] &&
        !(isReadonly && 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;
    }
    //创建代理对象并返回
    //对象类型和集合类型的handler不一样
    const proxy = new Proxy(target, targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers);
    proxyMap.set(target, proxy);
    return proxy;
}

如果reactive的内容不是对象则直接返回该数据,如果是对象,判断是否已经有代理对象,有直接返回。再判断对象的类型,只有Object, Array, Map, Set, WeakMap, WeakSet才能被代理。根据代理对象类型是 1Object, Array)还是 2 来根据不同的handler生成代理对象并返回。并在已代理集合(proxyMap)中加入该代理对象 proxyMap.set(target, proxy);

下面再看看代理对象的handler拦截的get方法内容:

function createGetter(isReadonly = false, shallow = false) {
    return function get(target, key, receiver) {
    	//获取代理对象是否被代理过的标志位
        if (key === "__v_isReactive" /* IS_REACTIVE */) {
            return !isReadonly;
        }
        //获取代理对象是否只读的标志位
        else if (key === "__v_isReadonly" /* IS_READONLY */) {
            return isReadonly;
        }
        //获取代理对象是否是浅响应数据的标志位
        else if (key === "__v_isShallow" /* IS_SHALLOW */) {
            return shallow;
        }
        //获取原始数据
        else if (key === "__v_raw" /* RAW */ &&
            receiver ===
                (isReadonly
                    ? shallow
                        ? shallowReadonlyMap
                        : readonlyMap
                    : shallow
                        ? shallowReactiveMap
                        : reactiveMap).get(target)) {
            return target;
        }
        const targetIsArray = isArray(target);
        if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
            return Reflect.get(arrayInstrumentations, key, receiver);
        }
        const res = Reflect.get(target, key, receiver);
        if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
            return res;
        }
        if (!isReadonly) {
            track(target, "get" /* GET */, key);
        }
        if (shallow) {
            return res;
        }
        if (isRef(res)) {
            // ref unwrapping - skip unwrap for Array + integer key.
            return targetIsArray && isIntegerKey(key) ? res : res.value;
        }
        if (isObject(res)) {
            // Convert returned value into a proxy as well. we do the isObject check
            // here to avoid invalid value warning. Also need to lazy access readonly
            // and reactive here to avoid circular dependency.
            return isReadonly ? readonly(res) : reactive(res);
        }
        return res;
    };
}

如果获取的是内置的几个标志位属性即返回对应值,只读属性或浅层响应数据直接返回,如果是数组有指定的处理函数,如果是对象,track(target, “get”, key)进行依赖收集
以下是set方法拦截的实现

function createSetter(shallow = false) {
    return function set(target, key, value, receiver) {
        let oldValue = target[key];
        if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
            return false;
        }
        if (!shallow && !isReadonly(value)) {
            if (!isShallow(value)) {
                value = toRaw(value);
                oldValue = toRaw(oldValue);
            }
            if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
                oldValue.value = value;
                return true;
            }
        }
        const hadKey = isArray(target) && isIntegerKey(key)
            ? Number(key) < target.length
            : hasOwn(target, key);
        const result = Reflect.set(target, key, value, receiver);
        //如果目标是在原始原型链上的东西,就不要触发
        if (target === toRaw(receiver)) {
        	//
            if (!hadKey) {
                trigger(target, "add" /* ADD */, key, value);
            }
            else if (hasChanged(value, oldValue)) {
                trigger(target, "set" /* SET */, key, value, oldValue);
            }
        }
        return result;
    };
}

const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
如果是只读或者ref类型的数据,set操作不赋值,如果是浅层响应,直接赋值,否则再使用Reflect进行set操作,并判断新值与旧值是否相等,不等则执行trigger通知更新。

总结

在定义响应式对象时可以使用reactive,但是对于基础类型的数据,是无法使用reactive来监听其变化的,这就要用到ref了,下面一篇就来讨论ref的实现原理及使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会尽力回答你的问题。首先,我们需要了解Vue3的响应式系统是如何工作的。Vue3使用了一个名为`Reactive`的函数来实现响应式。 `Reactive`函数的作用是将一个普通的JavaScript对象转换成响应式的对象。当响应式对象的属性被修改时,所有依赖该属性的地方都会自动更新。 下面是`Reactive`函数的实现: ```javascript function Reactive(obj) { const handlers = { get(target, prop, receiver) { const value = Reflect.get(target, prop, receiver); track(target, prop); return isObject(value) ? Reactive(value) : value; }, set(target, prop, value, receiver) { const oldValue = Reflect.get(target, prop, receiver); let result = true; if (oldValue !== value) { result = Reflect.set(target, prop, value, receiver); trigger(target, prop); } return result; }, deleteProperty(target, prop) { const result = Reflect.deleteProperty(target, prop); trigger(target, prop); return result; } }; return new Proxy(obj, handlers); } ``` `Reactive`函数接受一个普通的JavaScript对象作为参数,返回一个响应式的对象。在实现中,我们使用了ES6的Proxy对象来实现响应式。 在`get`处理器中,我们使用了`track`函数来收集依赖。`track`函数的作用是将当前正在执行的计算函数添加到依赖列表中。 在`set`处理器中,我们首先获取旧值,然后判断新值是否与旧值相同。如果不同,我们使用`trigger`函数来触发更新。`trigger`函数的作用是遍历依赖列表,执行所有计算函数。 在`deleteProperty`处理器中,我们使用`trigger`函数来触发更新,因为删除属性也可能导致依赖更新。 在以上代码中,我们还使用了`isObject`函数来判断一个值是否为对象。该函数的实现如下: ```javascript function isObject(value) { return typeof value === 'object' && value !== null; } ``` 这个函数非常简单,它只是判断一个值是否为对象。如果是对象,我们就递归调用`Reactive`函数来将该对象转换成响应式。 总之,这就是Vue3的响应式系统的实现原理。通过`Reactive`函数和Proxy对象,我们可以将一个普通的JavaScript对象转换成响应式的对象,并实现自动更新。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值