• 解析Vue3源码(一)——reactive


    解析Vue3源码(一)——reactive

    前言

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

    vue2
    data: {
    	obj: {}
    }
    vue3 
    let obj = reactive({});
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    如果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;
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    如果获取的是内置的几个标志位属性即返回对应值,只读属性或浅层响应数据直接返回,如果是数组有指定的处理函数,如果是对象,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;
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

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

    总结

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

  • 相关阅读:
    Numpy.array数组学习笔记(数组结构详解,数组操作方法,数组读取方法,数组计算方法,数组画图方法,数组存储方法)
    tiup dm template
    前端框架Bootstrap
    鸿蒙应用布局ArkUI【基础运用案例】
    Google Authenticator 算法
    主备延迟的问题
    ThingsBoard开源物联网平台介绍
    项目经理如何顺利推进项目:做好任务规划,合理安排时间
    第21节-PhotoShop基础课程-渐变工具
    Pytorch LSTM 长短期记忆网络
  • 原文地址:https://blog.csdn.net/qq_41028148/article/details/127620323