【Vue3 源码解析】reactive 全家桶

2023-09-20 16:58:10
// 泛型约束:只能传入引用类型
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
  )
}

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

  1. 函数签名:

    • function reactive<T extends object>(target: T): UnwrapNestedRefs<T>:这是 reactive 函数的类型签名,它接受一个泛型参数 T,表示目标对象的类型,并返回一个与目标对象类型相同的深度响应式代理对象。UnwrapNestedRefs<T> 类型用于解包嵌套的 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
}

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

  1. 函数签名:

    • function createReactiveObject(target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any>, proxyMap: WeakMap<Target, any>):这是 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 响应式系统的核心函数之一,它用于创建深度响应式代理对象。函数会检查目标对象是否已经被代理过,如果已经被代理则直接返回,避免不必要的重复代理。如果目标对象是在白名单中的可代理类型,那么就创建一个代理对象,并将其缓存,以便后续使用。
更多推荐

企业工程项目管理系统源码(三控:进度组织、质量安全、预算资金成本、二平台:招采、设计管理)

工程项目管理软件(工程项目管理系统)对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营,全过程、全方位的对项目进行综合管理工程项目各模块及其功能点清单一、系统管理1、数据字典:实现对数据字典标签的增删改查操作2、编码管理:实现对系统编码的增删改查操作3、用户管理:管理和查看用户角

2023/9/14 -- C++/QT

作业:仿照Vector实现MyVector,最主要实现二倍扩容#include<iostream>usingnamespacestd;template<typenameT>classMyVector{private:T*data;size_tsize;size_tV_capacity;public://无参构造MyVe

想了解RPA看这篇就够了(全面又清晰)

想了解RPA看这篇就够了(全面又清晰)本文目录如下:(1)什么是RPA(2)RPA技术发展路线(3)RPA应用场景(4)RPA对比人工的优势(5)RPA的核心能力(1)什么是RPARPA(RoboticProcessAutomation,机器人流程自动化)是一种通过软件机器人模拟和执行规则化、重复性的任务来提高工作效率

大模型重构行业,百度网盘再度抢跑?

随着ChatGPT爆火出圈,AI大模型逐渐渗透到各个行业和领域,诸多大厂也纷纷发布了自己的大模型产品。而在国内AI大模型的市场上,百度的文心大模型无疑是其中较为有名的一个。于是,在国内大厂都在探索如何在大模型时代取得更多优势时,百度大模型也不再局限于某个特定领域,而是将目光放在了网盘应用上。自百度网盘经历“百盘大战”进

Vue3中watch用法

在Vue3中的组合式API中,watch的作用和Vue2中的watch作用是一样的,他们都是用来监听响应式状态发生变化的,当响应式状态发生变化时,都会触发一个回调函数。当需要在数据变化时执行异步或开销较大的操作时,computed是无法操作异步数据的,所以需要使用watch进行侦听。侦听器watch作用是侦听一个或多个

斗地主案例及一些实现规则

4.斗地主发牌4.1案例介绍按照斗地主的规则,完成洗牌发牌的动作。具体规则:使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。4.2案例分析准备牌:牌可以设计为一个ArrayList<String>,每个字符串为一张牌。每张牌由花色数字两部分组成,我们可以使用花色集合与数字集合嵌套迭

Visual Studio复制、拷贝C++项目与第三方库配置信息到新的项目中

本文介绍在VisualStudio软件中,复制一个已有的、配置过多种第三方库的C++项目,将其拷贝为一个新的项目,同时使得新项目可以直接使用原有项目中配置好的各类**C++**配置、第三方库等的方法。在撰写C++代码时,如果需要用到他人撰写的第三方库,那么每次新建一个项目时都需要重新配置一次环境,相对比较麻烦;而如果我

c语言每日一练(15)

前言:每日一练系列,每一期都包含5道选择题,2道编程题,博主会尽可能详细地进行讲解,令初学者也能听的清晰。每日一练系列会持续更新,上学期间将看学业情况更新。五道选择题:1、程序运行的结果为()#include<stdio.h>intmain(){intpad=0;intpAd=0;intsum=5;pad=5;pAd=

SpringMVC之JSR303和拦截器

一.什么是JSR303二.JSR303常用注解作用使用导入pom.xml在实体类相对应的属性中增加注解用来指定校验在hpjyController里面新加以下代码修改eidt.jsp测试结果​编辑二.拦截器什么是拦截器拦截器与过滤器的区别应用场景日志记录:拦截器可以用于记录请求的相关信息,如请求的URL、请求参数、请求的

Springmvc之JSR303和拦截器

JSR303拦截器1.JSR303什么是JSR303JSR是JavaSpecificationRequests的缩写,意思是Java规范提案。是指向JCP(JavaCommunityProcess)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界

第九章(1):循环神经网络与pytorch示例(RNN实现股价预测)

第九章(1):循环神经网络与pytorch示例(RNN实现股价预测)作者:安静到无声个人主页作者简介:人工智能和硬件设计博士生、CSDN与阿里云开发者博客专家,多项比赛获奖者,发表SCI论文多篇。Thanks♪(・ω・)ノ如果觉得文章不错或能帮助到你学习,可以点赞👍收藏📁评论📒+关注哦!o( ̄▽ ̄)d欢迎大家来到

热文推荐