当前位置: 首页 > news >正文

Vue3源码reactivity响应式篇之Reactive

概览

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.dtcms.com/a/344988.html

相关文章:

  • 阿里云日志服务与Splunk集成方案(Splunk Add-on方式)实战
  • GitGithub相关(自用,持续更新update 8/23)
  • 通义万相:AI生视频提示词生成秘籍/指南
  • 高空作业智能安全带如何监控使用异常行为
  • Linux 下的网络编程
  • Linux笔记8——shell编程基础-2
  • ROS学习笔记1-幻宇机器人为模板
  • Windows11 家庭版永久解密BitLocker加密移动硬盘
  • 【Java并发编程】Java多线程深度解析:状态、通信与停止线程的全面指南
  • RK3506-PWM计数功能
  • c#实现鼠标mousemove事件抽稀,避免大数据阻塞网络
  • 【COMSOL】Comsol学习案例时的心得记录分享(三)
  • 罗技鼠标驱动下载教程 多种方法详细说明
  • 排序---插入排序
  • CS 创世 SD NAND 助力 T-BOX:破解智能汽车数字中枢的存储密码
  • 110、【OS】【Nuttx】【周边】效果呈现方案解析:查找最新构建件
  • C++/QT 开发技能树详解
  • 钉钉 Stream 模式SpringBoot接入配置与事件监听
  • Maxscript如何清理3dMax场景?
  • react样式问题
  • git旧仓库迁移到新仓库
  • [系统架构设计师]安全架构设计理论与实践(十八)
  • Web3与AI语境下的审美积累:HAQQ品牌识别解析
  • 多人编程新方式:cpolar 让 OpenHands 远程开发更轻松
  • 区块链技术原理(17)-以太坊网络
  • SpringBoot中的条件注解
  • 常用三角函数公式推导体系
  • LLM应用场景能力边界趋势全览
  • 从系统修复到硬件检测的技术实测
  • [antv-x6] 文档链接