MVVM原理(代理data)

const arrayProto = Array.prototype;

function defArrayFunc(obj, func, namespace, vm) {
    Object.defineProperty(obj, func, {
        configurable: true,
        enumerable: true, // 当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中
        // 触发枚举属性中的属性或者触发数组相关事件后会被捕获到
        value(...args) { // args是操作数组时的添加/删除的对象/其他类型的值的数组,例如push(2), args为[2]
            let original = arrayProto[func]; // 获取数组原型中对应func的函数,例如func为push时,original为function push(){}
            const result = original.apply(this, args); // 调用original函数,this是代理的数组
            console.log(getNameSpace(namespace, ""));
            return result;
        }
    })
}

function proxyArr(vm, arr, namespace) {
    let obj = {
        eleType: "Array",
        toString() {
            let result = "";
            for (let i = 0; i < arr.length; i++) {
                result += arr[i] + ", ";
            }
            return result.substring(0, result.length - 2);
        },
        push() {
        },
        pop() {
        },
        shift() {
        },
        unshift() {
        }
    };
    defArrayFunc.call(vm, obj, "push", namespace, vm);
    defArrayFunc.call(vm, obj, "pop", namespace, vm);
    defArrayFunc.call(vm, obj, "shift", namespace, vm);
    defArrayFunc.call(vm, obj, "unshift", namespace, vm);
    arr.__proto__ = obj; // 这个加不加不影响
    return arr;
}

function constructObjectProxy(vm, obj, namespace) {
    // 通过es6 Proxy实现
    // let proxyObj = new Proxy(obj, {
    //     get(target, p, receiver) {
    //         console.log("=")
    //         return target[p];
    //     },
    //     set(target, p, value, receiver) {
    //         console.log("=")
    //         target[p] = value;
    //     }
    // });
    //
    // return proxyObj;

    // 下面通过Object.defineProperty实现
    let proxyObj = {};
    for (let prop in obj) {
        Object.defineProperty(proxyObj, prop, {
            configurable: true,
            get() {
                return obj[prop];
            },
            set(v) {
                console.log(getNameSpace(namespace, prop));
                obj[prop] = v;
            }
        });
        // 实现Vue中通过this.xxx来使用data中的属性
        Object.defineProperty(vm, prop, {
            configurable: true,
            get() {
                return obj[prop];
            },
            set(v) {
                console.log(getNameSpace(namespace, prop));
                obj[prop] = v;
            }
        });

        if (obj[prop] instanceof Object) {
            proxyObj[prop] = constructProxy(vm, obj[prop], getNameSpace(namespace, prop))
        }
    }
    return proxyObj;
}

/**
 * 我们要知道Vue的data上哪个属性修改,我们才能对页面上的内容进行更新
 * 所以我们需要先捕获修改的这个事件,使用代理的方式来实现监听属性修改
 * 这里使用Object.defineProperty
 * @param vm
 * @param obj
 * @param namespace
 */
export function constructProxy(vm, obj, namespace) {// vm表示Vue对象,obj表示要代理的对象
    // 递归
    let proxyObj = null;
    if (obj instanceof Array) { // 判断这个对象是否为数组
        proxyObj = new Array(obj.length);
        for (let i = 0; i < obj.length; i++) {
            proxyObj[i] = constructProxy(vm, obj[i], namespace);
        }
        proxyObj = proxyArr(vm, obj, namespace);
    } else if (obj instanceof Object) { // 判断这个对象是否为对象
        // 构造代理对象
        proxyObj = constructObjectProxy(vm, obj, namespace);
    } else {
        throw new Error("error");
    }

    return proxyObj;
}

function getNameSpace(nowNameSpace, nowProp) {
    if (nowNameSpace === null || nowNameSpace === "") {
        return nowProp;
    } else if (nowProp == null || nowProp === "") {
        return nowNameSpace;
    } else {
        return nowNameSpace + "." + nowProp;
    }
}
原创文章 28 获赞 52 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq1123642601/article/details/104419999