//输入
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操作,将触发依赖。