概览

vue3中reactive用于将普通对象转换为响应式对象,它的实现原理是通过ProxyReflect来实现的。具体的实现文件参见packages\reactivity\src\reactive.ts。本文会介绍reactive的相关api如下:

  • reactive:将普通对象转换为响应式对象
  • readonly:将普通对象转换为只读响应式对象
  • isReactive:判断一个对象是否是响应式对象
  • isReadonly:判断一个对象是否是只读响应式对象
  • isShallow:判断一个对象是否是浅层响应式对象
  • isProxy:判断一个对象是否是代理对象
  • shallowReactive:创建一个浅层响应式对象
  • shallowReadonly:创建一个浅层只读响应式对象
  • markRaw:标记一个对象为原始对象,避免被转换为响应式对象
  • toReadonly:将一个响应式对象转换为只读响应式对象

源码分析

在分析reactive.ts的源码之前,先了解如下几个变量,它们分别是:

const reactiveMap = /* @__PURE__ */ new WeakMap(); // 响应式对象的缓存Map
const shallowReactiveMap = /* @__PURE__ */ new WeakMap(); // 浅层响应式对象的缓存Map
const readonlyMap = /* @__PURE__ */ new WeakMap(); // 只读响应式对象的缓存Map
const shallowReadonlyMap = /* @__PURE__ */ new WeakMap(); // 浅层只读响应式对象的缓存Map

isReadonly

isReadonly用于判断一个对象是否是只读响应式对象,它的实现如下:

function isReadonly(value) {return !!(value && value["__v_isReadonly"]);
}

isReadonly就是判断参数value__v_isReadonly属性值的布尔值,若为true则表示是只读对象;否则不是只读对象。

reactive

reactive的实现如下:

function reactive(target) {// 判断target是否是只读对象,若是,则直接返回if (isReadonly(target)) {return target;}// 调用 createReactiveObject 函数创建响应式对象,并返回return createReactiveObject(target, // 目标对象false, // 是否只读,默认为falsemutableHandlers, // 普通对象的代理处理函数mutableCollectionHandlers, // 集合对象的代理处理函数reactiveMap // 响应式对象的缓存Map);
}
createReactiveObject

reactive的核心实现是createReactiveObject函数,它的实现如下:

function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {// 判断target是否是对象,若不是,则弹出警告,并直接返回if (!isObject(target)) {{warn(`value cannot be made ${isReadonly2 ? "readonly" : "reactive"}: ${String(target)}`);}return target;}if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) {return target;}const targetType = getTargetType(target);if (targetType === 0 /* INVALID */) {return target;}const existingProxy = proxyMap.get(target);if (existingProxy) {return existingProxy;}const proxy = new Proxy(target,targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers);proxyMap.set(target, proxy);return proxy;
}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 */;}
}
function getTargetType(value) {return value["__v_skip"] || !Object.isExtensible(value) ? 0 /* INVALID */ : targetTypeMap(toRawType(value));
}

createReactiveObject函数接受5个参数,依次为:目标对象target、是否只读isReadonly2、基本类型处理函数baseHandlers、集合对象的代理处理函数collectionHandlers、响应式对象的缓存proxyMap

createReactiveObject在确保target为对象后,会检测target是一个响应式对象,若是则直接返回;然后会从缓存proxyMap中获取target的代理对象,若存在则直接返回该代理对象;否则调用getTargetType函数判断目标对象target的类型,若类型为INVALID,则直接返回target;否则根据类型创建代理对象,并将其缓存到proxyMap中,最后返回该代理对象。

getTargetType的实现也在上面,若target存在__v_skip属性且为true,或者target不是可扩展的对象,则返回INVALID;否则根据target的类型调用targetTypeMap函数返回类型。

由上targetTypeMap函数可知,targetTypeMap函数根据target的类型返回一个数字,分别表示:

  • 0INVALID,表示target不是一个对象
  • 1COMMON,表示target是一个普通对象或数组
  • 2COLLECTION,表示target是一个集合对象

综上,可以理解createReactiveObject函数根据target创建代理对象的逻辑如下:

  • target为普通对象或数组,则创建普通对象的代理对象,使用baseHandlers处理函数;
  • targetMap/Set/WeakMap/WeakSet,则创建集合对象的代理对象,使用collectionHandlers处理函数;
  • 其余情况,则直接返回target

关于处理器baseHandlerscollectionHandlers的实现,会在后面的章节中介绍。

readonly/shallowReactive/shallowReadonly

readonly/shallowReactive/shallowReadonly的实现如下:

function readonly(target) {return createReactiveObject(target,true, // 表示只读readonlyHandlers, // 普通对象的只读代理处理函数readonlyCollectionHandlers, // 集合对象的只读代理处理函数readonlyMap // 只读响应式对象的缓存readonlyMap);
}function shallowReactive(target) {return createReactiveObject(target,false, // 不是只读shallowReactiveHandlers, // 普通对象的浅层响应式代理处理函数shallowCollectionHandlers, // 集合对象的浅层响应式代理处理函数shallowReactiveMap // 浅层响应式对象的缓存shallowReactiveMap);
}function shallowReadonly(target) {return createReactiveObject(target,true, // 表示只读shallowReadonlyHandlers, // 普通对象的浅层只读代理处理函数shallowReadonlyCollectionHandlers, // 集合对象的浅层只读代理处理函数shallowReadonlyMap // 浅层只读响应式对象的缓存shallowReadonlyMap);
}

readonly的实现和reactive的实现类似,不同就是调用createReactiveObject函数时,传参不同。

类似的还有shallowReactiveshallowReadonly

它们的不同如下所示

函数只读浅层缓存普通对象的代理方法集合对象的代理方法
reactivereactiveMapbaseHandlerscollectionHandlers
shallowReactiveshallowReactiveMapshallowReactiveHandlersshallowCollectionHandlers
readonlyreadonlyMapreadonlyHandlersreadonlyCollectionHandlers
shallowReadonlyshallowReadonlyMapshallowReadonlyHandlersshallowReadonlyCollectionHandlers

isReactive

isReactive的实现如下:

function isReactive(value) {if (isReadonly(value)) {return isReactive(value["__v_raw"]);}return !!(value && value["__v_isReactive"]);
}

isReactive会先判断value是否为只读响应式数据,若为只读响应式数据,则会递归调用isReactive函数判断value的原始数据是否为响应式数据;否则,会判断value是否为响应式数据,若为响应式数据,则返回true,否则返回false

isShallow / isProxy / isReadonly

function isShallow(value) {return !!(value && value["__v_isShallow"]);
}
function isProxy(value) {return value ? !!value["__v_raw"] : false;
}
function isReadonly(value) {return !!(value && value["__v_isReadonly"]);
}

isShallow/isProxy/isReadonly的实现都比较简单,都是判断参数是否存在某个属性,若存在则返回true,否则返回false。它们读取的属性__v_isShallow/__v_raw/__v_isReadonly实际上都是针对代理对象的,而它们的逻辑处理也是在处理器方法中实现的,后续会讲到

markRaw

markRaw用于标记对象为原始对象,这种对象不会被转换为响应式对象,也不会被代理,其实现如下:

function markRaw(value) {if (!hasOwn(value, "__v_skip") && Object.isExtensible(value)) {def(value, "__v_skip", true);}return value;
}

markRaw首先会判断对象是否可扩展,若可扩展,则会在对象上定义一个__v_skip属性,打一个标记,值为true;最后返回该对象。使用reactive(target)创建响应式对象时,若target不可扩展,则在调用createReactiveObject时,其内部调用getTargetType的返回值就是0.

def内部就是调用Object.defineProperty定义对象上属性

toReadonly

toReadonly用于将响应式对象转换为只读对象,其实现如下:

const toReadonly = (value) => isObject(value) ? readonly(value) : value;

toReadonly会判断参数是否为对象,若为是,则会调用readonly函数将参数转换为只读响应式对象,并返回该代理对象;否则,直接返回参数。这和toReactive很类似,只是toReactive会将参数转换为响应式对象,而toReadonly会将参数转换为只读响应式对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/919737.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/919737.shtml
英文地址,请注明出处:http://en.pswp.cn/news/919737.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

初识数据结构——Map和Set:哈希表与二叉搜索树的魔法对决

数据结构专栏 ⬅(click) 大家好!我是你们的老朋友——想不明白的过度思考者!今天我们要一起探索Java中两个神奇的数据结构:Map和Set!准备好了吗?让我们开始这场魔法之旅吧!🎩 🎯 先…

Unreal Engine UStaticMeshComponent

UnrealUnreal Engine - UStaticMeshComponent🏛 定义🏛 类继承⚡ 关键特性⚙️ 常见配置🛠️ 使用方法📚 在 C 中使用📚 在蓝图中使用🎮 典型应用场景📚 常见子类与用途📝 小结Unrea…

demo 汽车之家(渲染-筛选-排序-模块抽离数据)

效果图展示:代码截图注释详情实现笔记总体目标(按需求点对照代码)数据模块化、整体渲染框架、筛选/排序的高亮与行为,全部已在 Index.ets CarData.ets 落地。下面按图片需求 2~4 点逐条总结,并给出关键代码定位与“为…

双重机器学习DML介绍

本文参考: [1]文心一言回答; 一、核心原理与数学框架 双重机器学习(Double Machine Learning, DML)由Chernozhukov等学者于2018年提出,是一种结合机器学习与传统计量经济学的因果推断框架。其核心目标是在高维数据和非…

【图像算法 - 21】慧眼识虫:基于深度学习与OpenCV的农田害虫智能识别系统

摘要: 在现代农业生产中,病虫害是影响作物产量和品质的关键因素之一。传统的害虫识别依赖人工巡查,效率低、成本高且易出错。本文将介绍如何利用深度学习与OpenCV构建一套高效的农田害虫智能识别系统。该系统能够自动识别10类常见农业害虫&a…

循环神经网络实战:GRU 对比 LSTM 的中文情感分析(三)

循环神经网络实战:GRU 对比 LSTM 的中文情感分析(三) 文章目录循环神经网络实战:GRU 对比 LSTM 的中文情感分析(三)前言数据准备(与 LSTM 相同)模型搭建(GRU)…

学习游戏制作记录(制作提示框以及使用键盘切换UI)8.21

1.制作装备提示框创建提示框,添加文本子对象,用来描述名称,类型以及属性加成挂载垂直分配组件和文本大小适配组件,这样图像会根据文本大小来调整自己创建UI_ItemTip脚本并挂载在文本框上:[SerializeField] private Tex…

chapter07_初始化和销毁方法

一、简介 一个Bean,在进行实例化之后,需要进行两种初始化 初始化属性,由PropertyValues进行赋值初始化方法,由ApplicationContext统一调用,例如加载配置文件 Bean的初始化与销毁,共有三种方式(注…

open webui源码分析6-Function

一、Functions简介 可以把Tools作为依赖于外部服务的插件,Functions就是内部插件,二者都是用来增强open webui的能力的。Functions是轻量的,高度可定制的,并且是用纯Python编写的,所以你可以自由地创建任何东西——从新…

C2039 “unref“:不是“osgEarth::Symbology::Style”的成员 问题分析及解决方法

在osgEarth2.10中实现多线段连续测量功能时,遇到下图中的错误; 经过测试和验证,主要问题出现在下图圈出代码的定义上 图22-1 对于22-1中的两个变量这样定义是错误的。因为Style类没有继承自osg::Referenced,因此不能与osg::ref_ptr配合使用

GitHub 热榜项目 - 日榜(2025-08-19)

GitHub 热榜项目 - 日榜(2025-08-19) 生成于:2025-08-19 统计摘要 共发现热门项目:12 个 榜单类型:日榜 本期热点趋势总结 本期GitHub热榜呈现三大技术热点:1)AI原生开发持续爆发,Archon OS、Parlant等…

ingress 配置ssl证书

模拟环境举例&#xff1a; # 生成带 OU 的证书配置文件 cat > csr.conf <<EOF [ req ] default_bits 2048 prompt no default_md sha256 distinguished_name dn[ dn ] C CN ST Beijing L Beijing O YourCompany, Inc. # 组织名称 (必填) OU DevOps De…

Pandas 合并数据集:concat 和 append

文章目录Pandas 合并数据集&#xff1a;concat 和 append回顾&#xff1a;NumPy 数组的拼接使用 pd.concat 进行简单拼接重复索引将重复索引视为错误忽略索引添加多级索引&#xff08;MultiIndex&#xff09;键使用连接&#xff08;Join&#xff09;方式拼接append 方法Pandas …

2025年5月架构设计师综合知识真题回顾,附参考答案、解析及所涉知识点(七)

本文主要回顾2025年上半年(2025-5-24)系统架构设计师考试上午综合知识科目的选择题,同时附带参考答案、解析和所涉知识点。 2025年5月架构设计师综合知识真题回顾,附参考答案、解析及所涉知识点(一) 2025年5月架构设计师综合知识真题回顾,附参考答案、解析及所涉知识点(…

面向RF设计人员的微带贴片天线计算器

微带贴片天线和阵列可能是仅次于单极天线和偶极天线的最简单的天线设计。这些天线也很容易集成到PCB中&#xff0c;因此通常用于5G天线阵列和雷达等高级系统。这些天线阵列在基谐模式和高阶模式下也遵循一组简单的设计方程&#xff0c;因此您甚至可以在不使用仿真工具的情况下设…

明基RD280U编程显示器深度测评:码农的「第二块键盘」竟然会发光?

文章目录前言一、开箱篇&#xff1a;当理工男遇到「俄罗斯套娃式包装」二、外观篇&#xff1a;深空灰的「代码容器」1. 桌面变形记2. 保护肩颈的人体工学设计三、显示篇&#xff1a;给代码做「光子嫩肤」1. 28寸超大大屏 3:2屏比 4K超清2.专业编程模式&#xff0c;让代码一目…

算法114. 二叉树展开为链表

题目&#xff1a;给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历 顺序相同。…

智慧能源管理系统:点亮山东零碳园区的绿色引擎

一、概述在全球积极践行“双碳”目标的时代浪潮下&#xff0c;山东作为经济大省&#xff0c;正全力推动产业的绿色变革&#xff0c;零碳园区建设成为其中的关键一环。《山东省零碳园区建设方案》明确规划&#xff0c;到2027年建成15个左右省级零碳园区 &#xff0c;到2030年进一…

分布式日志分析平台(ELFK 与 EFK)理论

一、日志分析平台核心概念在分布式系统中&#xff0c;日志是系统运行状态监控、问题排查和业务分析的重要依据。随着系统规模扩大&#xff0c;单机日志管理方式已无法满足需求&#xff0c;分布式日志分析平台应运而生。其核心目标是实现日志的集中收集、统一处理、高效存储和可…

CoreShop微信小程序商城框架开启多租户-添加一个WPF客户端以便进行本地操作--读取店铺信息(6)

本节内容&#xff0c;使用登录的token进行店铺信息读取&#xff0c;顺利的话&#xff0c;进行EXCEL上传测试。 1。在后台编写 读取店铺信息代码 1.1 查看原来铺店信息在什么位置&#xff0c;店铺的表格为CoreCmsStore#region 获取列表// POST: Api/CoreCmsStore/GetPageList///…