解析Vue3源码(一)——reactive

解析Vue3源码(一)——reactive

    • 前言
    • 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的实现原理及使用

你可能感兴趣的:(vue3解析,javascript)