Vue3响应式系统的原理——02.加入注册副作用函数的机制

74 阅读2分钟

首先回顾下响应式的原理

  1. 在get操作时,将响应式函数(能够修改页面的副作用函数)加入到集合中。
  2. 在set操作时,将函数从集合中拿出并执行副作用函数,达到修改页面数据的实现效果。

在01中,我们硬编码了响应式函数,一旦该函数不是effect了,该响应式系统就不能正常工作。所以需要在以下两处优化代码。

    get(target,key){
        bucket.add(effect);
        return target[key];
    }
    
    
    function effect(){
        document.body.innerText = obj.text;
    }

1.改造effect函数

首先定义一个全局变量,每次执行effect函数时将传入的fn赋值给activeEffect,此时无论fn是匿名函数还是普通函数。

    let activeEffect;
    //通过effect函数来注册响应式函数
    function effect(fn){
        activeEffect = fn;
        fn()
    }

2.改造代理的get

当读取代理对象的属性时,加入集合的不再是effect,而是全局变量activeEffect

    get(target,key){
        if(activeEffect){
            //无论读取的是哪个属性,都会将副作用函数加入桶中
            bucket.add(activeEffect);
        }     
        return target[key];
    },

3.完整源码

    const data = {text:"hello world"};
    //定义存放副作用函数的集合
    const bucket = new Set();
    //对原始数据进行代理
    const obj = new Proxy(data,{
        //读取值时,将副作用函数加入桶中
        get(target,key){
            if(activeEffect){
                //无论读取的是哪个属性,都会将副作用函数加入桶中
                bucket.add(activeEffect);
            }     
            return target[key];
        },
        //改变值时进行操作的拦截
        set(target,key,newVal){
            target[key] = newVal;
            bucket.forEach(fn => fn());
            return true;
        }
    })
    let activeEffect;
    //通过effect函数来注册响应式函数
    function effect(fn){
        activeEffect = fn;
        fn()
    }
    
    effect(() => {
        console.log("effect run");
        document.body.innerText = obj.text;
    })

    setTimeout(() => {
        obj.text = "响应式的改变"
    },2000)

4.总结:没有在副作用函数和目标字段(如text)间建立联系

  • 1.无论读取哪个属性都会将副作用函数activeEffect加入集合中。
  • 2.只要触发set都会执行副作用函数。
    setTimeout(() => {
        //同样会触发响应式,因为触发了代理对象的setter
        obj.noExsit = "响应式的改变"
    },2000)