vue3响应式原理

81 阅读1分钟

vue3的响应式是采用Proxy来拦击一个对象,并且在访问对象属性时进行依赖收集。在设置对象属性时,将收集到的依赖执行。vue在进行模版编译时,会将模版编译成渲染函数。如果某个节点使用了响应式对象的属性值,那么就会将这个节点对象的渲染函数自动添加到响应式对象的属性依赖中。

// 用来保存当前编译时,使用了响应式对象属性的render函数。
let activeEffect = null
// 用来保存所有响应式对象的依赖
let targetMap = new WeakMap();
// 
function effect(fn) {
    // 将render函数赋予全局的变量
    activeEffect = fn;
    // 执行render函数
    activeEffect();
    // 执行完毕后需要置空
    activeEffect = null
}
// 为当前对象的某个属性收集依赖
function track(target, key) {
    // 如果访问属性时,没有对于的节点render函数,就不需要收集依赖比如console.log(refData.value)。这种使用方法,是不需要收集依赖的。
    if (!activeEffect) {
        return
    }
    // 获取当前对象的所有依赖,如果不存在则新建一个map用来保存
    let depsMap = targetMap.get(target);
    if (!depsMap) {
        targetMap.set(target, depsMap = new Map());
    }
    // 获取当前对象某个属性的依赖,如果不存在则用新建
    let deps = depsMap.get(key);
    if (!deps) {
        depsMap.set(key, deps = new Set())
    }
    // 收集依赖
    deps.add(activeEffect);
}

function reactive(target) {
    const handler = {
        get(target, key, receiver) {
            // 在访问属性时,收集依赖
            track(target, key);
            console.log(`访问了${key}属性`)
            return Reflect.get(target, key, receiver)
        },
        set(target, key, value, receiver) {
            // 在设置属性时,触发所有依赖性执行
            console.log(`将${key}由->${target[key]}->设置成->${value}`)
            Reflect.set(target, key, value, receiver)
            trigger(target, key)
        }
    }
    return new Proxy(target, handler)
}

function trigger(target, key) {
    let depsMap = targetMap.get(target);
    let deps = depsMap.get(key);
    deps.forEach(fn => {
        fn()
    })
}

const data = {
    name: 'tom',
    age: 123
}
const proxyData = reactive(data);

let effect1 = () => {
    console.log('执行了effect1')
    return `name is ${proxyData.name}`
}
effect(effect1)

proxyData.name = 'jerry';