探索Vue3响应式API之Ref(三)

163 阅读2分钟
//输入
const {ref} = VueReactivity;
const counter = ref({a:{b:{c:123}}});
console.log(counter.value.a);
counter.value.a = 1;

看输入,先读取了counter.value.a的值,这会触发proxy的handlers的get函数,看下get函数都做了什么。

function createGetter(isReadonly = false, shallow = false) {    
    return function get(target, key, receiver) {      
                ...      
                const targetIsArray = isArray(target);      
                if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
                    return Reflect.get(arrayInstrumentations, key, receiver);      
                }      
                const res = Reflect.get(target, key, receiver);      
                ...      
                if (!isReadonly) {               
                    track(target, 'get' /* GET */, key);      
                }      
                ...      
                ...      
                if (isObject(res)) {        
                    // Convert returned value into a proxy as well. we do the isObject check        
                    // here to avoid invalid value warning. Also need to lazy access readonly        
                    // and reactive here to avoid circular dependency.        
                    return isReadonly ? readonly(res) : reactive(res);      
                }      
                return res;    
            }  
}

可以看到函数内判断了target是不是数组,因为我们传入的是一个深层嵌套的对象,所以显然不会进入语句内。

const res = Reflect.get(target, key, receiver);

Reflect.get,返回结果是a的属性,因为我们这里的key是a,res应该是{b:{c:1}}。isReadonly为false,所以会进入语句内,执行track函数,track函数的作用就是进行依赖收集。因为res是一个对象,可以看到最后又将res进行响应式化,res又变成了一个proxy。

也就是我们读一个proxy内的某个属性,这个属性最后又会被proxy化。

这样可以保证不管是多么复杂的对象,其中的每个属性只要被用到,都将变成响应式的。

counter.value.a = 1 ;

这条语句触发了handlers的set函数,看看set函数都干了什么。

function createSetter(shallow = false) {    
    return function set(target, key, value, receiver) {      
            const oldValue = target[key];      
            ...      
            const hadKey = isArray(target) && isIntegerKey(key) ? 
                Number(key) < target.length : hasOwn(target, key);      
            const result = Reflect.set(target, key, value, receiver);      
            // don't trigger if target is something up in the prototype chain of original 
             if (target === toRaw(receiver)) {        
                if (!hadKey) {         
                     trigger(target, 'add' /* ADD */, key, value);      
              } else if (hasChanged(value, oldValue)) {          
                    trigger(target, 'set' /* SET */, key, value, oldValue);        
                }      
             }      
            return result;    
    }  
}

oldValue就是a的值{b:{c:1}},hadKey明显为true。

const result = Reflect.set(target, key, value, receiver);

这条代码的作用是重新设置key(也就是a)的值,这里target会发生变化,proxy也会发生变化。

可以看出判断的条件分支最后会执行trigger函数,这个函数的作用就是触发所有的依赖。

总结:

一个proxy内的属性的get操作,将收集依赖。
一个proxy内的属性的set操作,将触发依赖。