• 【Vue3 源码解析】reactive 全家桶


    // 泛型约束:只能传入引用类型
    export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
    // 判断只读,否则创建reactive响应式对象
    export function reactive(target: object) {
      // if trying to observe a readonly proxy, return the readonly version.
      if (isReadonly(target)) {
        return target
      }
      return createReactiveObject(
        target,
        false,
        mutableHandlers,
        mutableCollectionHandlers,
        reactiveMap
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这段代码是 Vue 3 中的 reactive 函数的实现部分,它用于将一个对象转化为深度响应式代理对象。下面是这段代码的详细解释:

    1. 函数签名:

      • function reactive(target: T): UnwrapNestedRefs:这是 reactive 函数的类型签名,它接受一个泛型参数 T,表示目标对象的类型,并返回一个与目标对象类型相同的深度响应式代理对象UnwrapNestedRefs 类型用于解包嵌套的 Ref,以获得原始值。

      • function reactive(target: object):这是 reactive 函数的实际实现。

    2. 实现逻辑:

      • 首先,函数检查目标对象 target 是否已经被标记为只读(readonly)。如果是只读的,就直接返回目标对象本身,因为只读对象不应该被转化为响应式对象。

      • 如果目标对象不是只读的,那么就调用 createReactiveObject 函数来创建深度响应式代理对象。这里传递了一些参数:

        • target:要代理的目标对象。
        • false:表示不是只读的,即要创建可修改的代理对象。
        • mutableHandlers:处理深度响应式代理对象属性变化的处理器集合。
        • mutableCollectionHandlers:处理集合类型属性变化的处理器集合。
        • reactiveMap:WeakMap,用于存储对象和它们的响应式代理之间的映射关系。
    3. 总结:

      • 这段代码实现了 reactive 函数的核心逻辑,它负责创建一个深度响应式代理对象,该对象能够追踪目标对象属性的变化,并在变化时通知相关视图进行更新。如果目标对象已经是只读对象,那么函数不会再对其进行代理,而是直接返回只读对象本身。
    function createReactiveObject(
      target: Target,
      isReadonly: boolean,
      baseHandlers: ProxyHandler<any>,
      collectionHandlers: ProxyHandler<any>,
      proxyMap: WeakMap<Target, any>
      //WeakMap的 键 只能是一个object类型的数据,并且WeakMap的键名所指向的对象,不计入垃圾回收机制
      //它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。
      //因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。
      //也就是说,一旦不再需要,WeakMap里面的键名对象和所对应的键值对会自动消失,不用手动删除引用
    ) {
      // 如果传入的是普通的基本数据类型
      if (!isObject(target)) {
        if (__DEV__) {
          console.warn(`value cannot be made reactive: ${String(target)}`)
        }
        return target
      }
      // target is already a Proxy, return it.
      // exception: calling readonly() on a reactive object
      // target 已经被代理过,并且不是为了将响应式对象变为只读,则直接返回
      if (
        target[ReactiveFlags.RAW] &&
        !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
      ) {
        return target
      }
      // target already has corresponding Proxy
      // 从缓存(readonlyMap,reactiveMap)中查找,如果已经被代理过则直接返回
      const existingProxy = proxyMap.get(target)
      if (existingProxy) {
        return existingProxy
      }
      // only specific value types can be observed.
      // 如果在白名单直接返回 例如 __skip__
      const targetType = getTargetType(target)
      if (targetType === TargetType.INVALID) {
        return target
      }
      // 如果以上条件都没触发,则会进行一个proxy代理
      const proxy = new Proxy(
        target,
        targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
      )
      // 缓存新代理后的对象
      proxyMap.set(target, proxy)
      return proxy
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    这段代码是 Vue 3 中用于创建响应式代理对象的核心逻辑,它实现了 createReactiveObject 函数。下面是这段代码的详细解释:

    1. 函数签名:

      • function createReactiveObject(target: Target, isReadonly: boolean, baseHandlers: ProxyHandler, collectionHandlers: ProxyHandler, proxyMap: WeakMap):这是 createReactiveObject 函数的类型签名。它接受以下参数:
        • target:要代理的目标对象。
        • isReadonly:一个布尔值,表示是否要创建只读的代理对象。
        • baseHandlers:一个处理常规对象属性变化的处理器集合。
        • collectionHandlers:一个处理集合对象属性变化的处理器集合。
        • proxyMap:一个 WeakMap,用于存储对象与它们的代理之间的映射关系。
    2. 实现逻辑:

      • 首先,函数会检查 target 是否是一个普通的基本数据类型,如果是,会在开发环境下发出警告,并返回 target 本身。这是为了确保只有对象才会被代理,基本数据类型不会被代理。

      • 接着,函数检查 target 是否已经被代理过,如果是并且不是为了将响应式对象变成只读的(即 isReadonlyfalse),则直接返回 target 本身。这是一种性能优化,避免重复代理已经被代理过的对象。

      • 如果 target 还没有被代理过,函数会继续进行以下操作:

        • 调用 getTargetType 函数,该函数根据目标对象的类型,返回一个值,指示目标对象的类型。具体来说,如果 target 是普通对象或数组,它会返回 TargetType.COMMON;如果是集合对象(如 Map、Set 等),则返回 TargetType.COLLECTION;否则,返回 TargetType.INVALID

        • 如果 targetTypeTargetType.INVALID,则说明 target 的类型不在白名单中,不能被代理,直接返回 target 本身。

        • 最后,使用 new Proxy 创建一个代理对象 proxy,并根据 targetType 选择相应的处理器集合(baseHandlerscollectionHandlers)来处理属性的变化。然后,将 proxy 缓存到 proxyMap 中,以便下次再次代理相同的 target 时可以直接返回缓存的 proxy

    3. 总结:

      • createReactiveObject 是 Vue 3 响应式系统的核心函数之一,它用于创建深度响应式代理对象。函数会检查目标对象是否已经被代理过,如果已经被代理则直接返回,避免不必要的重复代理。如果目标对象是在白名单中的可代理类型,那么就创建一个代理对象,并将其缓存,以便后续使用。
  • 相关阅读:
    统计学习---第一章
    c# 逆变 / 协变
    DOM及DOM相关操作
    zookeeper实现分布式锁
    EraseNet:End-to-End Text Removal in the wild
    创建一个基本的win32窗口
    RS485通讯方式-详解
    【Unity性能优化】静态资源优化——Audio优化
    Flink PostgreSQL CDC配置和常见问题
    11_刻意练习精讲
  • 原文地址:https://blog.csdn.net/XiugongHao/article/details/133086477