上一篇我们探索了简单数据类型ref之后的结果,还记得整个过程吗?我们生成了一个RefImpl对象,对象上面挂载了__v_isRef、_rawValue、_shallow、_value四个属性,且_rawValue和_value的值相等。当我们触发get获取操作时,返回_value的值,触发set更新操作时,同时更新_rawValue和value的值(两个值相等)。
这篇我们要了解的是ref对复杂类型数据是如何处理的。
//输入
const {ref} = VueReactivity;
const counter = ref({
a:{
b:{
c:123
}
}
});
传进去一个深层嵌套的对象,看看发生了什么。
function createRef(rawValue, shallow = false) {
if (isRef(rawValue)) {
return rawValue;
}
return new RefImpl(rawValue, shallow);
}
还是new RefImpl。
class RefImpl {
constructor(_rawValue, _shallow = false) {
this._rawValue = _rawValue;
this._shallow = _shallow;
this.__v_isRef = true;
this._value = _shallow ? _rawValue : convert(_rawValue);
}
...
}
可以看到_rawValue仍然储存的是我们传进去的值,但是_value会有所变化。
我们看看convert这次会有什么不一样。
const convert = val => (isObject(val) ? reactive(val) : val)
可以发现,对于复杂数据类型,会执行reactive函数,把它变成响应式的,这与之前的简单数据类型处理已经不同,看看reactive函数都做了什么。
function reactive(target) {
// if trying to observe a readonly proxy, return the readonly version.
if (target && target['__v_isReadonly' /* IS_READONLY */]) {
return target;
}
return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers);
}
第一个判断语句判断是否有__v_isReadonly,显然不会执行,看看后面的createReactiveObject函数,从名字看,创建响应式对象,wooo~~~,未知的神秘都在里面,看看都有什么。
function createReactiveObject(
target, isReadonly, baseHandlers, collectionHandlers )
{
...
...
// target already has corresponding Proxy
const proxyMap = isReadonly ? readonlyMap : reactiveMap;
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
// only a whitelist of value types can be observed.
const targetType = getTargetType(target);
if (targetType === 0 /* INVALID */) {
return target;
}
const proxy = new Proxy(target, targetType === 2
/* COLLECTION */ ? collectionHandlers : baseHandlers);
proxyMap.set(target, proxy);
return proxy;
}
前两个判断语句不会执行,所以我这里省略。先打印看下传递的四个值分别是什么。
前两个很好理解,一个是我们传递进去的值,一个是默认给的false,但是后面两个就有点一头雾水了。what???发生了什么???mutableHandlers和mutableCollectionHandlers对象里面都包裹着函数,但是不要紧,这些函数现在还没有调用,都不会执行,所以我们之后再慢慢解决,先看createReactiveObject代码一一执行发生了什么。
//isReadonly默认为false,所以proxyMap = reactiveMap
const proxyMap = isReadonly ? readonlyMap : reactiveMap;
const reactiveMap = new WeakMap();
const readonlyMap = new WeakMap();
可以看出来proxyMap现在是一个空的WeakMap集合。
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
但是reactiveMap和readonlyMap里面存放的都是什么?上面的代码可以推测出存放的就是我们ref时传进去的对象(对象作为键,而对应的值就是proxy),可以想象一下,如果有多个不同的对象被ref,reactiveMap和readonlyMap里面都会存放多个对应的对象和对象的proxy。
const targetType = getTargetType(target);
看一下getTargetType函数。
function getTargetType(value) {
return value['__v_skip' /* SKIP */] || !Object.isExtensible(value)
? 0 /* INVALID */ : targetTypeMap(toRawType(value));
}
因为我们输入的值是一个深层嵌套的对象,里面没有__v_skip属性并且是可扩展的,所以返回的值是targetTypeMap(toRawType(value))。
先看下toRawType函数的作用。
const toRawType = value => {
// extract "RawType" from strings like "[object RawType]"
return toTypeString(value).slice(8, -1);
}
const objectToString = Object.prototype.toString;
const toTypeString = value => objectToString.call(value);
可以看出toRawType(value) === Object.prototype.toString.call(value).slice(8, -1),为了判断对象是哪种类别,这里因为我们传入的是Object,所以toRawType(value)的结果为object,再看看targetTypeMap函数。
function targetTypeMap(rawType) {
switch (rawType) {
case 'Object': ;
case 'Array':
return 1 /* COMMON */;
case 'Map': ;
case 'Set': ;
case 'WeakMap': ;
case 'WeakSet': ;
return 2 /* COLLECTION */;
default:
return 0 /* INVALID */; } }
可以看出来函数的作用是Object和Array返回1,Map、Set、WeakMap、WeakSet返回2,否则都是0。所以结果是1。
if (targetType === 0 /* INVALID */) { return target; }
从接下来的判断语句可以推测出ref对复杂类型的处理只接受Object、Array、Map、Set、WeakMap、WeakSet这几种类型。
const proxy = new Proxy( target,
targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers; );
看到这里,我们终于接触到了proxy。来看看都干了些啥。
可以看出来Proxy接受两个参数,一个是target,就是我们传入的值,现在我们传入的是一个深层嵌套的对象。第二个参数可以发现它是动态的,如果是Object和Array类型将传入baseHandlers,否则传入collectionHandlers。
如之前讲到的,baseHandlers和collectionHandlers中有一些函数,但是它们现在没有被调用,所以我们不用关心太多,只需要知道此时已经生成了一个proxy,更多的细节在之后再去研究。
proxyMap.set(target, proxy);
return proxy;
最后两行代码,将target和proxy键值对放到readonlyMap或者reactiveMap中,然后将proxy返回。
我们研究了这么久createReactiveObject函数,还记得是在哪里调用的吗?在reactive函数内,最终会返回给this._value,也就是RefImpl对象的_value。看下打印结果。
可以发现,和简单数据类型对比,复杂类型的ref其实只有一个地方有所不同,就是_value,_value的值就是一个Proxy。
让我们梳理一下整个过程:
我们先将一个深层嵌套的对象传递进去,会通过reactive函数来将传递进去的值变成可响应式的,而要做的就是先判断传递进去值是什么类型,根据不同的类型来使用不同的handlers(要么是collectionHandlers要么是baseHandlers),最终根据value,以及不同的handlers,来生成一个proxy。然后以键值对的方式将value和proxy存储到readonlyMap或者reactiveMap中(它们是一个WeakMap集合),最后,将proxy赋值给this._value,也就是RefImpl的_value。
比较简单数据类型和复杂数据类型的ref有什么不同:
简单数据类型的_value和_rawValue相同,复杂数据类型的_rawValue是我们输入的值,_value是根据输入的值和Handlers生成的proxy,在过程中还会以键值对的方式将value和proxy推入到一个WeakMap集合中存储。