Vue3.0 双向数据绑定原理代码

2020-03-18  本文已影响0人  alokka
let toProxy = new WeakMap(); // 放置的是原对象:代理过的对象
let toRaw = new WeakMap(); // 被代理过的对象: 原对象

function isObject(val){
    return typeof val === 'object' && val !== null;
}

function hasOwn(target,key) {
    return target.hasOwnProperty(key);
}

function reactive(target) {
    // 创建响应式对象
    return createReactiveObject(target);
}

// 创建响应式对象
function createReactiveObject(target) {
    if(!isObject(target)) {
        return target;
    }

    let proxy = toProxy.get(target);

    // if(proxy){ // 如果已经代理过了 就将代理过的结果返回即可
    //     console.log('proxy!!!');
    //     return proxy;
    // }
    // if(toRaw.has(target)){ // 防止代理的过的对象再次被代理
    //     console.log('toRaw!!!');
    //     return target;
    // }

    proxy ? proxy : null; // 检测是否被代理过
    toRaw.has(target) ? target : null; // 防止代理过的对象被多次代理

    let baseHandler = {
        get(target,key,receiver) {
            console.log('get!!!');
            let res = Reflect.get(target,key,receiver);
            // 收集订阅 把当前的 key 和 effect 对应起来
            track(target, key); // 如果目标上的 这个 key 变化了 重新让数组中的 effect 执行即可
            return isObject(res) ? reactive(res) : res;
        },
        set(target,key,value,receiver) {
            console.log('set!!!');
            let hadKey = hasOwn(target,key); // 判断这个属性以前没有
            let oldValue = target[key];
            let res = Reflect.set(target,key,value,receiver);
            if(!hadKey){
                trigger(target,'add',key);
            }else if(oldValue !== value){ // 这里表述属性 更改过了
                trigger(target,'set',key);
            } // 为了屏蔽 无意义的修改
            // 如果设置没成功 如果这个对象不可以被更改 writable
            return res;
        },
        deleteProperty(target,key) {
            let res = Reflect.deleteProperty(target,key);
            return res;
        }
    };

    let observed = new Proxy(target,baseHandler);
    toProxy.set(target,observed);
    toRaw.set(observed,target);
    return observed;
}

// 栈 先进后出
let activeEffectStacks = []; // 栈型结果

let targetsMap = new WeakMap();
function track(target,key) {
    let effect = activeEffectStacks[activeEffectStacks.length - 1];
    if(effect) { // 有对应关系 才创建关联
        console.log('track!!!');
        let depsMap = targetsMap.get(target);
        if(!depsMap) {
            targetsMap.set(target,depsMap = new Map());
        }
        let deps = depsMap.get(key);
        if(!deps) {
            depsMap.set(key,deps = new Set());
        }
        if(!deps.has(effect)) {
            deps.add(effect);
        }
    }
}

function trigger(target,type,key) {
    let depsMap = targetsMap.get(target);

    if(depsMap) {
        let deps = depsMap.get(key);
        if(deps) { // 将当前 key 对应的 effect 依次执行
            deps.forEach(effect => {
                effect();
            })
        }
    }
}


function effect(fn) {
    // 需要把 fn 变成响应式函数
    let effect = createReactiveEffect(fn);
    effect(); // 默认先执行一次
}

function createReactiveEffect(fn) {
    let effect = function() { // 响应式的 effect
        return run(effect,fn); // 1. 让 fn 执行  2. 把 effect 存到栈中
    }
    return effect;
}

function run(effect,fn) { // 运行 fn 并将 effect 存起来
    try {
        activeEffectStacks.push(effect);
        fn(); // 利用 js 是单线程
    }finally {
        activeEffectStacks.pop(effect);
    }
}

// 依赖收集 发布订阅
// 1. 处理对象
let obj = reactive({name:'zf'});
effect(() => { // effect 会执行两次 默认先执行一次 之后依赖的数据变化了 再次执行
    // console.log(obj.name);
})


obj.name = 'lokka';
console.log(obj.name);
上一篇 下一篇

猜你喜欢

热点阅读