容器结构:

  1. WeakMap:{
  2. Object: Map(ObjectKey,Set([]))
  3. }

结构图片

image.png

  • WeakMap内部 紫色区域
  • Map 灰色区域
  • ObjectKey 粉色区域
  • Set 黄色区域

问题的思考:

为什么用WeakMap?

1、WeakMap键名所引用的对象都是弱引用!而且不在JavaScript垃圾回收机制不将其引用考虑在内。就是说,没有引用就自动被清除。

  1. let weak = new WeakMap();
  2. let obj = { a: { a: 1 } };
  3. let otherObj = obj;
  4. let objSubRef = obj.a;
  5. weak.set(obj, "存放");
  6. obj = null;
  7. console.log(weak.get(obj)); // undefined
  8. console.log(weak.get(otherObj)); // 存放
  9. otherObj = null;
  10. console.log(weak.get(otherObj)); // undefined
  11. console.log(objSubRef); // {a:1}
  12. //若在垃圾回收机制中,objSubRef 存在 obj 里的引用,垃圾机制是不回收的。

2、WeakMap只接受对象为key,每个数据的引用都是唯一,解决重复key的可能,可以统一存取。

为什么WeakMap的value是用Map来存取?

开发过程中,key不只是String,有可能是symbol等等。详情点击此处

为什么Map的value是用Set来存取?

1、存回调函数,Vue-3.0的 effect API 了解一下。

2、 Set的遍历顺序是按照插入顺序来运行。详情点击此处

容器的收集机制

image.png

track

  1. export function track(target: object, type: TrackOpTypes, key: unknown) {
  2. if (!shouldTrack || activeEffect === undefined) {
  3. return
  4. }
  5. let depsMap = targetMap.get(target)
  6. if (depsMap === void 0) {
  7. targetMap.set(target, (depsMap = new Map()))
  8. }
  9. let dep = depsMap.get(key)
  10. if (dep === void 0) {
  11. depsMap.set(key, (dep = new Set()))
  12. }
  13. if (!dep.has(activeEffect)) {
  14. dep.add(activeEffect)
  15. activeEffect.deps.push(dep)
  16. if (__DEV__ && activeEffect.options.onTrack) {
  17. activeEffect.options.onTrack({
  18. effect: activeEffect,
  19. target,
  20. type,
  21. key
  22. })
  23. }
  24. }
  25. }

track的
主要集中为两个方向服务:

  1. 容器的初始化与数据收集。
  1. let depsMap = targetMap.get(target)
  2. if (depsMap === void 0) {
  3. targetMap.set(target, (depsMap = new Map()))
  4. }
  5. let dep = depsMap.get(key)
  6. if (dep === void 0) {
  7. depsMap.set(key, (dep = new Set()))
  8. }
  1. 当前key是否与 effect API 组合使用?若是:把 effect 包裹的函数收集; effect 第二个参数是否有参,且为onTrack,若有,立刻调用。
  1. if (!dep.has(activeEffect)) {
  2. dep.add(activeEffect)
  3. activeEffect.deps.push(dep)
  4. if (__DEV__ && activeEffect.options.onTrack) {
  5. activeEffect.options.onTrack({
  6. effect: activeEffect,
  7. target,
  8. type,
  9. key
  10. })
  11. }
  12. }

trigger

  1. export function trigger(
  2. target: object,
  3. type: TriggerOpTypes,
  4. key?: unknown,
  5. extraInfo?: DebuggerEventExtraInfo
  6. ) {
  7. const depsMap = targetMap.get(target)
  8. if (depsMap === void 0) {
  9. // never been tracked
  10. return
  11. }
  12. const effects = new Set<ReactiveEffect>()
  13. const computedRunners = new Set<ReactiveEffect>()
  14. if (type === TriggerOpTypes.CLEAR) {
  15. // collection being cleared, trigger all effects for target
  16. depsMap.forEach(dep => {
  17. addRunners(effects, computedRunners, dep)
  18. })
  19. } else {
  20. // schedule runs for SET | ADD | DELETE
  21. if (key !== void 0) {
  22. addRunners(effects, computedRunners, depsMap.get(key))
  23. }
  24. // also run for iteration key on ADD | DELETE
  25. if (type === TriggerOpTypes.ADD || type === TriggerOpTypes.DELETE) {
  26. const iterationKey = isArray(target) ? 'length' : ITERATE_KEY
  27. addRunners(effects, computedRunners, depsMap.get(iterationKey))
  28. }
  29. }
  30. const run = (effect: ReactiveEffect) => {
  31. scheduleRun(effect, target, type, key, extraInfo)
  32. }
  33. // Important: computed effects must be run first so that computed getters
  34. // can be invalidated before any normal effects that depend on them are run.
  35. computedRunners.forEach(run)
  36. effects.forEach(run)
  37. }

主要为目标对应dep容器存储的函数的执行:

  1. 筛选computer组、普通函数组。
  2. 执行分先后,先computer组,后普通函数组。

:::tips 欢迎斧正! :::