Believel/blog

Vue3.0响应式原理实现—(reactive、effect)

Opened this issue · 0 comments

// Vue3.0 响应式原理
// WeakMap:类似于Map,但是键值只能是对象,键值不会存放在内存中,所以不能用forEach遍历,不会在成内存泄漏
let toProxy = new WeakMap(); // 弱引用映射表,原对象:代理过的对象
let toRaw = new WeakMap(); // 被代理过的对象:原对象
// 判断是否是对象
function isObject(obj) {
    return (typeof obj === 'object') && obj !== 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) {
        return proxy
    }
    if (toRaw.get(target)) { // 防止代理过的对象再次代理
        return target
    }
    let handler = {
        get (target, key, receiver) {
            console.log('获取')
            let result = Reflect.get(target, key, receiver)

            // 收集依赖  订阅 把当前的key 和这个effect 对应起来
            track(target, key) // 如果目标上的 这个key变化了 重新让数组中的effect执行即可
            return isObject(result) ? reactive(result): result
        },
        set (target, key, value, receiver) {
            // 识别是新增属性还是修改属性
            const hadKey = hasOwn(target, key) // 判断属性之前有没有
            const oldValue = target[key] // 之前的值
            let res = Reflect.set(target, key, value, receiver)
            // console.log(key)
            if (!hadKey) {
                // console.log('新增属性')
                trigger(target, 'add', key)
            } else if (oldValue !== value){ // 这里表示属性已经修改过了
                // console.log('修改属性')
                trigger(target, 'set', key)
            } // 屏蔽无用的修改(这里是修改length长度,其实值已经更改过了)
            // 返回一个 bool 值
            return res
        },
        deleteProperty (target, key) {
            let res = Reflect.deleteProperty(target, key)
            console.log('删除')
            return res
        }
    }
    let observed = new Proxy(target, handler)
    toProxy.set(target, observed)
    toRaw.set(observed, target)
    return observed
}

// !代理对象
// let proxy = reactive({ name: { n: 'zf'}, age: 100})
// proxy.name.n
// delete proxy.age

// !如何实现对象本身或者代理对象多次代理
// let p1 = reactive(proxy)
// reactive(proxy)
// reactive(proxy)
// console.log(p1 === proxy)


// !代理数组
// let proxy = reactive([3, 5, 8])
// proxy.push(4)
// proxy[0] = 1
// proxy.length = 100

// 响应式
let activeEffectStacks = [];
// {
//     target: {
//         key: [fn, fn]
//     }
// }
let targetsMap = new WeakMap();
function track (target, key) {
    let effect = activeEffectStacks[activeEffectStacks.length - 1]
    if (effect) { // 有对应关系  才创建关联
        let depsMap = targetsMap.get(target)
        if (!depsMap) {
            targetsMap.set(target, depsMap = new Map());
        }
        let deps = targetsMap.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) {
            deps.forEach(effect => {
                effect()
            })
        }
    }
}
function effect(fn) {
    // 需要把fn这个函数变成响应式的函数
    let effect = createReactiveEffect(fn)
    effect(); // 默认先执行一次

}
function createReactiveEffect(fn) {
    // 这个就是创建的响应式的effect
    let effect = function() {
        // run函数的作用:1.让fn执行,2.把effect存到栈中
        return run(effect, fn)
    }
    return effect
}
function run(effect, fn) {
    try {
        activeEffectStacks.push(effect)
        fn()
    } finally {
        activeEffectStacks.pop()
    }
}
// !依赖收集  发布订阅
let obj = reactive({ name: 'ww'})
effect(() => { // effect默认会执行两次,默认先执行一次  之后依赖的数据变化了 会再次执行
    // console.log('更新视图')
    console.log(obj.name) // 会调用get方法
})
obj.name = 'qq'

images