本文已参与「新人创作礼」活动,一起开启掘金创作之路。
本文基于
Vue 3.2.30版本源码进行分析
为了增加可读性,会对源码进行删减、调整顺序、改变部分分支条件的操作,文中所有源码均可视作为伪代码
由于ts版本代码携带参数过多,不利于展示,大部分伪代码会取编译后的js代码,而不是原来的ts代码
本文内容
- Set数据响应式测试代码调试与Vue3对应源码总结
 - Map数据响应式测试代码调试与Vue3对应源码总结
 
本篇文章不对ref类型、shallow类型、readonly类型进行总结分析
本篇文章主要集中总结Vue3对于Map和Set数据一些常见方法代理时的源码处理,不深入探究原理
本文尽可能对源码中涉及到的读写操作的源码进行列举和总结,难免会有遗漏
前置说明
在上一篇Vue3源码-响应式系统-依赖收集和派发更新流程浅析文章中,我们梳理了整个响应式系统依赖收集和派发更新的流程,还简单地介绍了Proxy以及Reflect,明白了响应式的基本原理就是拦截target一些方法,比如get,比如set,然后进行依赖收集和派发更新
但是我们并没有对Vue3中响应式数据类型的分类,响应式数据常见属性的拦截等源码进行分析,本篇文章将基于Vue3源码各种非原始值的数据响应式进行总结,主要研究的源代码核心部分如下所示:
function reactive(target) {
    return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
}
function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers, proxyMap) {
    const targetType = getTargetType(target);
    if (targetType === 0 /* INVALID */) {
        return target;
    }
    const proxy = new Proxy(target, targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers);
    return proxy;
}
const mutableHandlers = {
    get,
    set,
    deleteProperty,
    has,
    ownKeys
};
const mutableCollectionHandlers = {
    get: /*#__PURE__*/ createInstrumentationGetter(false, false)
};
从new Proxy(target)中可以发现,响应式监听数据分为两种targetType=2以及targetType=1,从上面和下面代码块可以得知,当target=2时,即数据类型为Map/Set/WeapMap/WeapSet时,new Proxy(target, collectionHandlers),本文将基于collectionHandlers进行分析
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不同的是,我们直接从源码分析它们响应式的逻辑,而不从读/写方面分析
主要分为两个部分,第一个部分是get、size、has等等常规方法
const mutableInstrumentations = {
    get(key) {
        return get$1(this, key);
    },
    get size() {
        return size(this);
    },
    has: has$1,
    add,
    set: set$1,
    delete: deleteEntry,
    clear,
    forEach: createForEach(false, false)
};
第二部分是keys、values、entries等迭代方法
const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator];
iteratorMethods.forEach(method => {
    mutableInstrumentations[method] = createIterableMethod(method, false, false);
});
function createIterableMethod(method, isReadonly, isShallow) {
    return function (...args) {
        const rawTarget = toRaw(target);
        //......
        track(rawTarget, "iterate" /* ITERATE */, isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY);
        return {
            next() {
                const { value, done } = innerIterator.next();
                return done ? { value, done }
                    : { value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value), done };
            },
            [Symbol.iterator]() {
                return this;
            }
        };
    };
}
迭代器和可迭代对象
为下面的forEach、entries、keys、values的分析做理论准备
迭代器/迭代器协议 iterator protocol
迭代器协议定义了产生一系列值(无论是有限个还是无限个)的标准方式。当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值。
只有实现了一个拥有以下语义的 next() 方法,一个对象才能成为迭代器
| 属性 | 值 | 
|---|---|
next | 一个无参数的或者可以接受一个参数的函数,返回一个应当拥有以下两个属性的对象:done(boolean)next()方法必须返回一个对象,该对象应当有两个属性: done 和 value,如果返回了一个非对象值(比如 false 或 undefined),则会抛出一个 TypeError 异常("iterator.next() returned a non-object value")。 | 
可迭代对象 iterable protocol
要成为可迭代对象,一个对象必须实现@@iterator方法。这意味着对象(或者它原型链上的某个对象)必须有一个键为@@iterator的属性,可通过常量 Symbol.iterator访问该属性:
| 属性 | 值 | 
|---|---|
[Symbol.iterator] | 一个无参数的函数,其返回值为一个符合迭代器协议 的对象。  | 
示例说明
typeof aGeneratorObject.next;
// 返回"function", 因为有一个 next 方法,所以这是一个迭代器
typeof aGeneratorObject[Symbol.iterator];
// 返回"function", 因为有一个 @@iterator 方法,所以这是一个可迭代对象
aGeneratorObject[Symbol.iterator]() === aGeneratorObject;
// 返回 true,因为 @@iterator 方法返回自身(即迭代器),所以这是一个格式良好的可迭代对象
接收可迭代对象的内置API
很多 API 接受可迭代对象作为参数,例如:
- newMap([iterable])
 - new WeakMap([iterable])
 - new Set([iterable])
 - new WeakSet([iterable])
 - Promise.all(iterable)
 - Promise.race(iterable)
 - Array.from(iterable)
 
new Map([[1, 'a'], [2, 'b'], [3, 'c']]).get(2); // "b"
let myObj = {};
new WeakMap([
    [{}, 'a'],
    [myObj, 'b'],
    [{}, 'c']
]).get(myObj);             // "b"
new Set([1, 2, 3]).has(3); // true
new Set('123').has('2');   // true
new WeakSet(function* () {
    yield {}
    yield myObj
    yield {}
}()).has(myObj);           // true
可迭代对象的应用
一些语句和表达式需要可迭代对象,比如 for...of 循环、展开语法、yield*,和解构赋值
for(let value of ["a", "b", "c"]){
    console.log(value); // "a", "b", "c"
}
[..."abc"]; // ["a", "b", "c"]
function* gen() {
  yield* ["a", "b", "c"];
}
gen().next(); // { value: "a", done: false }
[a, b, c] = new Set(["a", "b", "c"]);
for....in和for....of的区别
for...in语句以任意顺序迭代一个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性(原型链上的key)
for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句,遍历可迭代对象定义要迭代的数据
forEach循环无法中途跳出,break和return都无法中断循环,而for...in和for...of可以 for...in是无序的,for...of是有序的
自定义实现一个可迭代对象和迭代器协议
核心结构
var myIterator = {
  next: function() {
    // ...
  },
  [Symbol.iterator]: function() {
    return this;
  }
};
简单示例
function MyArray(array) {
    let nextIndex = 0;
    return {
        next: function () {
            // 应该done:true将nextIndex置为0
            return nextIndex < array.length ? {
                value: array[nextIndex++],
                done: false
            } : {
                done: true
            };
        },
        [Symbol.iterator]: function () {
            return this;
        }
    };
}
var initArray = new MyArray(["我是第一个", "2", "我是第三个"]);
for (let value of initArray) {
    console.log(value);
}
forEach:proxy.forEach(function(value, key, target), thisArg)
测试代码
effect(() => {
    proxyMap.forEach((...args) => {
        console.warn("proxyMap.forEach的item结果是");
        console.info(...args);
    });
});
源码分析
- 触发
Proxy.get()响应,触发track(rawTarget, "iterate", ITERATE_KEY)的依赖收集 callback返回数据可能也是Object类型,包括key和value,都得使用toReactive(value)+toReactive(key)进行转化为响应式对象后返回forEach可以传递第二个参数thisArg,callback.call调用时需要显式callback.call(thisArg)forEach的第一个参数function(value, key, target)中第三个参数是触发forEach调用的对象,传递代理对象observed
function createForEach(isReadonly, isShallow) {
    return function forEach(callback, thisArg) {
        const observed = this;
        const target = observed["__v_raw" /* RAW */];
        const rawTarget = toRaw(target);
        track(rawTarget, "iterate" /* ITERATE */, ITERATE_KEY);
        return target.forEach((value, key) => {
            // important: make sure the callback is
            // 1. invoked with the reactive map as `this` and 3rd arg
            // 2. the value received should be a corresponding reactive/readonly.
            return callback.call(thisArg, toReactive(value), toReactive(key), observed);
        });
    };
}
entries/Symbol.iterator/keys/values
测试代码 & 原始方法逻辑分析
entries:Map和Set都会返回一个可迭代对象,可以使用for(let [key,value] of data.entries())进行遍历Map.entries()返回的是[key, value]数据Set.entries()返回的是[value, value]数据
keys:由于Set没有key,因此Set.keys()等同于Set.values(),Map.keys()获取所有的keysvalues:由于Set没有key,因此Set.keys()等同于Set.values(),Map.values()获取所有的valueSymbol.iterator:Set[Symbol.iterator]跟Set.entries()表现不一致,如下面代码块所示,Set[Symbol.iterator]更接近于Set.values,Set[Symbol.iterator] === Set.entries为falseMap[Symbol.iterator]与Map.entries()表现一致,Map[Symbol.iterator] === Map.entries为true
const mySet = new Set(); mySet.add("0");mySet.add("1");
const setIter = mySet[Symbol.iterator]();
console.log(setIter.next().value); // "0"
for(let value of mySet[Symbol.iterator]()) {
  // Symbol.iterator let...of 0
  // Symbol.iterator let...of 1
  console.warn("Symbol.iterator let...of", value);
}
const set1 = new Set(); set1.add("0");set1.add("1");
const iterator1 = set1.entries();
console.log(iterator1.next().value); // ['0', '0']
for(let value of set1.entries()) {
  // Symbol.iterator let...of ['0', '0']
  // Symbol.iterator let...of ['1', '1']
  console.warn("entries let...of", value);
}
源码分析
返回数据
- 根据上面上面迭代器和可迭代对象的理论分析,我们需要实现一个可迭代对象和实现迭代器协议,即
next()和[Symbol.iterator]() - 根据
entries/Symbol.iterator+Map进行数据的返回:[value[0], value[1]],返回数据都进行toReactive()响应式包裹 - 其余的
method=keys/values/Symbol.iterator+Set,都只返回一个数据,即value,返回数据都进行toReactive()响应式包裹 
依赖追踪
- 如果使用
keys,那么我们就只需要关注key的变化,不需要关注value的变化,因此使用MAP_KEY_ITERATE_KEY进行依赖追踪,触发track(rawTarget, "iterate", MAP_KEY_ITERATE_KEY) - 如果使用
values/entries/Symbol.iterator,都需要关注value的变化,因此使用ITERATE_KEY进行依赖追踪,触发track(rawTarget, "iterate", ITERATE_KEY) 
function createIterableMethod(method, isReadonly, isShallow) {
    return function (...args) {
        const target = this["__v_raw" /* RAW */];
        const rawTarget = toRaw(target);
        const targetIsMap = isMap(rawTarget);
        const isPair = method === 'entries' || (method === Symbol.iterator && targetIsMap);
        const isKeyOnly = method === 'keys' && targetIsMap;
        const innerIterator = target[method](...args);
        const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive;
        !isReadonly &&
            track(rawTarget, "iterate" /* ITERATE */, isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY);
        return {
            // iterator protocol
            next() {
                const { value, done } = innerIterator.next();
                return done
                    ? { value, done }
                    : {
                        value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value),
                        done
                    };
            },
            // iterable protocol
            [Symbol.iterator]() {
                return this;
            }
        };
    };
}
总结
根据Map/Set数据类型的特性,仿写对应的next()和[Symbol.iterator]实现迭代器和可迭代对象,在仿写的基础上,实现对应的依赖收集逻辑和返回数据响应式包裹逻辑
size:proxy.size
测试代码
effect(() => {
    console.log("map.size", proxyMap.size);
});
源码分析
触发Proxy.size()响应,然后进行track(target, "iterate", ITERATE_KEY)的依赖收集
跟
Object/Array不同的是,Map/Set不能通过代理对象访问属性,会报Uncaught TypeError: Method get Map.prototype.size called on incompatible receiver undefined的错误 因此Reflect.get()的第三个参数receiver需要显示更改为原始对象target
function size(target, isReadonly = false) {
    target = target["__v_raw" /* RAW */];
    track(toRaw(target), "iterate" /* ITERATE */, ITERATE_KEY);
    return Reflect.get(target, 'size', target);
}
get:proxy.get("key")
测试代码
const object = {
    "item1": 1,
    "item2": 333
}
const map = new Map(Object.entries(object));
const objectKey = { test: 1 };
const reactiveKey = reactive(objectKey);
map.set(reactiveKey, "testReactive");
const proxy = reactive(map);
const objectKeyProxy = { testProxy: 1 };
const reactiveKeyProxy = reactive(objectKeyProxy);
proxy.set(reactiveKeyProxy, "testProxyReactive");
effect(() => {
    console.log("map.get(普通number)", proxy.get(0));
    console.log("map.get(proxy对象)", proxy.get(reactiveKey));
    console.log("map.get(原始对象)", proxy.get(objectKey));
    console.log("map.get(proxy对象之后才set的)", proxy.get(reactiveKeyProxy));
});
代码分析
- 触发
Proxy.get()响应,如果key是响应式,需要进行key的track,即track(target, "get", key)+track(target, "get", rawKey)的依赖收集 - 触发
Proxy.get()响应,如果key不是响应式,则进行track(target, "get", rawKey)的依赖收集 - 除了上面的依赖收集之外,需要使用原始对象进行
get()方法的调用,即target.get(key) - 如果返回对象
target.get(key)是Object类型,需要转化为响应式的,调用reactive(value) - 由于
key可能是响应式的Object Key,因此需要判断has.call(rawTarget, key)是否为true,如果不为true,需要去进行has.call(rawTarget, rawKey)的判断,然后进行具体的get请求返回要求的值 
function get(target, key, isReadonly = false, isShallow = false) {
    target = target["__v_raw" /* RAW */];
    const rawTarget = toRaw(target);
    const rawKey = toRaw(key);
    if (key !== rawKey) {
        !isReadonly && track(rawTarget, "get" /* GET */, key);
    }
    !isReadonly && track(rawTarget, "get" /* GET */, rawKey);
    const { has } = getProto(rawTarget);
  	// const toReactive = (value) => isObject(value) ? reactive(value) : value;
    const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive;
    if (has.call(rawTarget, key)) {
        return wrap(target.get(key));
    }
    else if (has.call(rawTarget, rawKey)) {
        return wrap(target.get(rawKey));
    }
    else if (target !== rawTarget) {
        // #3602 readonly(reactive(Map))
        // ensure that the nested reactive `Map` can do tracking for itself
        target.get(key);
    }
}
has:proxy.has("key")
测试代码
const object = {
    "item1": 1,
    "item2": 333
}
const map = new Map(Object.entries(object));
const proxy = reactive(map);
effect(() => {
    console.log("map.has", proxy.has(4));
});
源码分析
- 触发
Proxy.has()响应,如果key是响应式,需要进行key的track,即track(target, "get", key)+track(target, "get", rawKey)的依赖收集 - 触发
Proxy.has()响应,如果key不是响应式,则进行track(target, "get", rawKey)的依赖收集 
function has(key, isReadonly = false) {
    const target = this["__v_raw" /* RAW */];
    const rawTarget = toRaw(target);
    const rawKey = toRaw(key);
    if (key !== rawKey) {
        !isReadonly && track(rawTarget, "has" /* HAS */, key);
    }
    !isReadonly && track(rawTarget, "has" /* HAS */, rawKey);
    return key === rawKey
        ? target.has(key)
        : target.has(key) || target.has(rawKey);
}
Set.add
测试代码
effect(() => {
    console.log("Set.add", proxySet.add(5));
});
源码分析
- 触发
Proxy.add()响应,如果value不存在,使用原生Set.add()方法,然后trigger(target, "add", value, value)的依赖收集- 触发
Proxy.size()所依赖收集的ITERATE_KEY,触发所有涉及到size的effects重新执行(元素新增,size也新增了) - 由于不是
Map数据,因此不会触发MAP_KEY_ITERATE_KEY,MAP_KEY_ITERATE_KEY关联的是Map的key数据,是Map的forEach以及for...in所收集的依赖 
 - 触发
 - 如果
value存在,则什么都不操作 
function add(value) {
    value = toRaw(value);
    const target = toRaw(this);
    const proto = getProto(target);
    const hadKey = proto.has.call(target, value);
    if (!hadKey) {
        target.add(value);
        trigger(target, "add" /* ADD */, value, value);
    }
    return this;
}
function trigger(target, type, key, newValue, oldValue, oldTarget) {
    if (key !== void 0) {
        deps.push(depsMap.get(key));
    }
    switch (type) {
        case "add" /* ADD */:
            if (!isArray(target)) {
                deps.push(depsMap.get(ITERATE_KEY));
                if (isMap(target)) {
                    deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
                }
            }
            break;
    }
}
Map.set
测试代码
effect(()=> {
  console.log("Map.set", proxy.set(0, 44444));
});
源码分析
Map.set会判断是否存在key,如果存在,则触发trigger(target, "add" /* ADD */, key, value)Map.set会判断是否存在key,如果不存在,则触发trigger(target, "set" /* SET */, key, value, oldValue)
function set(key, value) {
    value = toRaw(value);
    const target = toRaw(this);
    const { has, get } = getProto(target);
    let hadKey = has.call(target, key);
    if (!hadKey) {
        key = toRaw(key);
        hadKey = has.call(target, key);
    }
    else {
        checkIdentityKeys(target, has, key);
    }
    const oldValue = get.call(target, key);
    target.set(key, value);
    if (!hadKey) {
        trigger(target, "add" /* ADD */, key, value);
    }
    else if (hasChanged(value, oldValue)) {
        trigger(target, "set" /* SET */, key, value, oldValue);
    }
    return this;
}
新增key以及value
trigger(target, "add" /* ADD */, key, value):
depsMap.get(key)的所有effecs重新执行,此时key=Map数据中新的key- 触发跟
size相关的ITERATE_KEY的effecs重新执行(增加key会影响Map.size的属性,而循环遍历,比如forEach、for..of都需要获取Map.size,因为遍历得要知道到底需要遍历item为几个) - 触发跟
keys相关的MAP_KEY_ITERATE_KEY的effecs重新执行(由上面entries/Symbol.iterator/keys/values的分析可以知道,这个MAP_KEY_ITERATE_KEY是为了关联key集合而产生的,因为对于一些遍历来说,比如for(let key of map.keys()),我们只需要关注keys,不需要关注values的变化) 
function trigger(target, type, key, newValue, oldValue, oldTarget) {
    if (key !== void 0) {
        deps.push(depsMap.get(key));
    }
    switch (type) {
        case "add" /* ADD */:
            if (!isArray(target)) {
                deps.push(depsMap.get(ITERATE_KEY));
                if (isMap(target)) {
                    deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
                }
            }
    }
}
更新已经存在的key的value
trigger(target, "set" /* SET */, key, value, oldValue):
depsMap.get(key)的所有effecs重新执行- 跟
size相关的ITERATE_KEY的effecs重新执行 
function trigger(target, type, key, newValue, oldValue, oldTarget) {
    if (key !== void 0) {
        deps.push(depsMap.get(key));
    }
    switch (type) {
        case "set" /* SET */:
            if (isMap(target)) {
                deps.push(depsMap.get(ITERATE_KEY));
            }
            break;
    }
}
按照常识来说,更新已经存在的key的value不应该触发跟size相关的ITERATE_KEY的effecs重新执行,但是源码确实这么做了,如下面示例代码,每次更新已经存在的key的value后,虽然proxyMap.size不会变化,但是effect会重新执行
已经提了github vue3-issue,由于本文基于
Vue 3.2.30版本源码进行分析,就算后续版本修复该问题,Vue 3.2.30版本仍然也是有这个问题,因此这里总结为:在Vue 3.2.30版本下,任何Map.set操作都会触发跟size相关的ITERATE_KEY的effecs重新执行,包括Map.size/forEach、for..of等遍历方法
const map = new Map();
map.set("item1", 1);
map.set("item2", 2);
const proxyMap = reactive(map);
effect(()=> {
 console.info("proxyMap.size", proxyMap.size);
});
onMounted(()=> {
 setInterval(() => {
   proxyMap.set("item1", new Date().getTime());
 }, 2000);
});
delete
测试代码
effect(() => {
    console.log("size.delete", proxy.delete("item2"));
});
源码分析
- 触发
Proxy.delete(key)响应,直接调用原始对象target.delete(key) Map.delete会判断是否存在key/toRaw(key),如果存在,则触发trigger(target, "delete" /* DELETE */, key, undefined, oldValue)Map.delete会判断是否存在key,如果不存在,则判断toRaw(key)是否存在,如果不存在,则什么都不操作
function deleteEntry(key) {
    const target = toRaw(this);
    const { has, get } = getProto(target);
    let hadKey = has.call(target, key);
    if (!hadKey) {
        key = toRaw(key);
        hadKey = has.call(target, key);
    } else {
        // 检测 key === toRaw(key),进行警告
        checkIdentityKeys(target, has, key);
    }
    const oldValue = get ? get.call(target, key) : undefined;
    // forward the operation before queueing reactions
    const result = target.delete(key);
    if (hadKey) {
        trigger(target, "delete" /* DELETE */, key, undefined, oldValue);
    }
    return result;
}
派发更新
trigger(target, "delete" /* DELETE */, key, undefined, oldValue);:
depsMap.get(key)的所有effecs重新执行- 触发跟
size相关的ITERATE_KEY的effecs重新执行(删除key会影响Map.size的属性,而循环遍历,比如forEach、for..of都需要获取Map.size,因为遍历得要知道到底需要遍历item为几个) - 触发跟
keys相关的MAP_KEY_ITERATE_KEY的effecs重新执行(由上面entries/Symbol.iterator/keys/values的分析可以知道,这个MAP_KEY_ITERATE_KEY是为了关联key集合而产生的,因为对于一些遍历来说,比如for(let key of map.keys()),我们只需要关注keys,不需要关注values的变化) 
function trigger(target, type, key, newValue, oldValue, oldTarget) {
    if (key !== void 0) {
        deps.push(depsMap.get(key));
    }
    switch (type) {
        case "delete" /* DELETE */:
            if (!isArray(target)) {
                deps.push(depsMap.get(ITERATE_KEY));
                if (isMap(target)) {
                    deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
                }
            }
            break;
    }
}
clear
测试代码
effect(() => {
    console.log("Map.clear", proxy.clear());
});
源码分析
- 触发
Proxy.clear()响应,直接调用原始对象target.clear() - 然后判断没清空之前的
size是否为0,如果不为0,则trigger(target, "clear" /* CLEAR */, undefined, undefined, oldTarget) 
function clear() {
    const target = toRaw(this);
    const hadItems = target.size !== 0;
    const oldTarget = isMap(target)
        ? new Map(target)
        : new Set(target)
        ;
    // forward the operation before queueing reactions
    const result = target.clear();
    if (hadItems) {
        trigger(target, "clear" /* CLEAR */, undefined, undefined, oldTarget);
    }
    return result;
}
派发更新
trigger(target, "clear" /* CLEAR */, undefined, undefined, oldTarget):
调用所有该对象的depsMap,触发该对象涉及到的所有effects重新执行
function trigger(target, type, key, newValue, oldValue, oldTarget) {
    const depsMap = targetMap.get(target);
    if (!depsMap) {
        // never been tracked
        return;
    }
    let deps = [];
    if (type === "clear" /* CLEAR */) {
        // collection being cleared
        // trigger all effects for target
        deps = [...depsMap.values()];
    }
}
总结
跟Object/Array不同,Map/Set无法调用Reflect.set(target, key, receiver)/Reflect.get(target, key, receiver)进行数据的操作,只能使用原生数据target进行一系列的操作,代理Map/Set多个方法时,主要有4个步骤:
- 转化
key/value为toRaw(),对key/toRaw(key)以及value/toRaw(value)进行处理 - 调用
Map/Set原生的方法进行数据的处理 - 根据方法适配不同的依赖收集和派发更新
 - 返回值应该判断是否需要
toReactive(object)包裹