《Vue进阶教程》第十六课:深入完善响应式系统之单例模式

   往期内容:

《Vue进阶教程》第五课:ref()函数详解(重点)

《Vue进阶教程》第六课:computed()函数详解(上)

《Vue进阶教程》第七课:computed()函数详解(下)

《Vue进阶教程》第八课:watch()函数的基本使用

《Vue进阶教程》第九课:watch()函数的高级使用

《Vue进阶教程》第十课:其它函数

《Vue进阶教程》第十一课:响应式系统介绍

《Vue进阶教程》第十二课:实现一对多

《Vue进阶教程》第十三课:实现依赖收集

《Vue进阶教程》第十四课:改进桶结构

《Vue进阶教程》第十五课:深入完善响应式系统之模块化

🤔思考

  1. 对于同一个源对象每次调用reactive返回的代理对象应该是一样的
  2. 对于一个已经代理过的对象再次代理应该返回的也应该是一样的

1) 实现单例

为了实现单例, 我们需要建立源对象->代理对象的映射关系

  • 如果存在映射, 说明已经代理过了, 直接返回
  • 如果不存在映射, 说明没有代理过, 创建一个新的代理对象返回

定义源对象->代理对象的映射表(使用WeakMap)

reactive.js

// 定义一个副作用函数桶, 存放所有的副作用函数. 每个元素都是一个副作用函数
// 修改 [state -> Map[name: Set(fn, fn), age: Set(fn, fn)], state1 -> Map]
const bucket = new WeakMap()// 建立一个映射表 target -> proxy
const reactiveMap = new WeakMap() // 新增// 定义一个全局变量, 保存当前正在执行的副作用函数
let activeEffect = nullfunction isObject(value) {return typeof value === 'object' && value !== null
}// 收集依赖
function track(target, key) {// 只有activeEffect有值时(保存的副作用函数), 才添加到桶中if (!activeEffect) returnlet depMap = bucket.get(target)if (!depMap) {depMap = new Map()bucket.set(target, depMap)}let depSet = depMap.get(key)if (!depSet) {depSet = new Set()depMap.set(key, depSet)}depSet.add(activeEffect)
}function trigger(target, key) {let depMap = bucket.get(target)if (!depMap) return// 从副作用函数桶中依次取出每一个元素(副作用函数)执行let depSet = depMap.get(key)if (depSet) {depSet.forEach((fn) => fn())}
}
/*** 创建响应式数据*  @param [object]: 普通对象*  @return [Proxy]: 代理对象*/
function reactive(data) {if (!isObject(data)) return// 如果映射表中存在了对应关系if (reactiveMap.has(data)) {// 返回data对应的代理对象return reactiveMap.get(data)}const proxy = new Proxy(data, {get(target, key) {// 在get操作时, 收集依赖track(target, key)return target[key]},set(target, key, value) {target[key] = value// 在set操作时, 触发副作用重新执行trigger(target, key)return true},})// 建立data(源对象)和proxy(代理对象)的映射关系reactiveMap.set(data, proxy)return proxy
}/*** 注册副作用函数*  @param [function]: 需要注册的 副作用函数*/
function effect(fn) {if (typeof fn !== 'function') return// 记录正在执行的副作用函数activeEffect = fn// 调用副作用函数fn()// 重置全局变量activeEffect = null
}

 测试用例

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><script src="./reactive.js"></script></head><body><script>const source = { name: 'hello' }const p = reactive(source)const p1 = reactive(source)console.log(p === p1) // true</script></body>
</html>

2) 实现重复代理

可以定义一个特殊的标识__v_isReactive

  • 如果存在该标识, 说明已经代理过, 直接返回
  • 如果不存在该标识, 说明没有被代理, 创建新的代理对象

示例

// 定义源对象->代理对象映射表
const reactiveMap = new WeakMap()// 定义一个副作用桶bucket
const bucket = new WeakMap() 
// 定义一个全局变量, 作于保存 `当前副作用函数`
let activeEffect = null// 收集依赖
function track(target, key) {// 根据不同的target, 获取对应的Maplet depMap = bucket.get(target)if (!depMap) {depMap = new Map()bucket.set(target, depMap)}let depSet = depMap.get(key)if (!depSet) {depSet = new Set()depMap.set(key, depSet)}depSet.add(activeEffect)
}// 触发执行
function trigger(target, key) {let depMap = bucket.get(target)if (!depMap) returnlet depSet = depMap.get(key)if (depSet) {// 如果对应的集合存在, 遍历集合中的每个函数depSet.forEach((fn) => fn())}
}/*** 定义响应式*  @param [object] : 普通对象*  @return [Proxy] : 代理对象*/
export function reactive(data) {// 如果传入的data不是一个普通对象, 不处理if (typeof data !== 'object' || data == null) returnif (reactiveMap.has(data)) {// 返回data对应的代理对象return reactiveMap.get(data)}// 如果存在标识, 说明data被代理过了if (data['__v_isReactive']) {return data}const proxy = new Proxy(data, {get(target, key) {if (key == '__v_isReactive') return true // 新增// console.log(`自定义访问${key}`)if (activeEffect != null) {// 收集依赖track(target, key)}return target[key]},set(target, key, value) {// console.log(`自定义设置${key}=${value}`)target[key] = value // 先更新值// 触发更新trigger(target, key)return true},})reactiveMap.set(data, proxy)return proxy
}/*** 注册副作用函数* @params [function]: 要注册的 副作用函数*/
export function effect(fn) {if (typeof fn !== 'function') return// 将当前注册的副作用函数 保存 到全局变量中activeEffect = fn// 执行当前副作用函数, 收集依赖fn()// 重置全局变量activeEffect = null
}

 测试用例

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><script src="./reactive.js"></script></head><body><script>const source = { name: 'hello' }const p = reactive(source)const p1 = reactive(p)console.log(p === p1) // true</script></body>
</html>

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

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

相关文章

生态学研究中,森林生态系统的结构、功能与稳定性是核心研究

在生态学研究中&#xff0c;森林生态系统的结构、功能与稳定性是核心研究内容之一。这些方面不仅关系到森林动态变化和物种多样性&#xff0c;还直接影响森林提供的生态服务功能及其应对环境变化的能力。森林生态系统的结构主要包括物种组成、树种多样性、树木的空间分布与密度…

基于UNITY3D的照片墙演示项目技术分享

unity实现超大图片墙演示,由于拥有海量图片&#xff0c;使用了CPU 多线程&#xff0c;unity dots技术&#xff0c;图片组成文字部分&#xff0c;使用了点阵图技术&#xff0c;提取文字像素。 &#xff08;关于点阵介绍&#xff09; 点阵字体是把每一个字符都分成1616或2424个点…

设计模式-访问者设计模式

介绍 访问者模式&#xff08;Visitor&#xff09;&#xff0c;表示一个作用于某对象结构中的各元素的操作&#xff0c;它使你可以在不改变个元素的类的前提下定义作用于这些元素的新操作。 问题&#xff1a;在一个机构里面有两种员工&#xff0c;1.Teacher 2.Engineer 员…

ISO/IEC 25010:2023 系统和软件的质量模型(产品质量模型)

本文是对ISO/IEC 25010 2023年底第2版&#xff1a;系统和软件工程-系统和软件质量要求与评估 (SQuaRE)-产品质量模型一文的转载&#xff0c;此外还参考了ISO/IEC 25019:2023(en) Systems and software engineering — Systems and software Quality Requirements and Evaluatio…

【零基础保姆级教程】制作自己的数据集——Labelimg的安装与使用及常见的报错解决方法

1.是什么 LabelImg 是一个图形化的图像标注工具&#xff0c;主要用于机器学习和计算机视觉领域中的数据预处理。它是由 GitHub 用户 tzutalin 开发的开源项目&#xff0c;基于 Python 和 Qt 框架编写。LabelImg 允许用户手动为图像添加边界框&#xff08;bounding boxes&#…

【java基础系列】实现数字的首位交换算法

在java中&#xff0c;手写实现一个数字的首位交换算法实现 实现效果 实现代码 核心业务代码 public static void main(String[] args) {int[] arr {1,2,3,4,5};int temp arr[0];for (int i 0; i < arr.length; i) {System.out.print(arr[i]);}System.out.println(&quo…

【java面向对象编程】第七弹----Object类、类变量与类方法

笔上得来终觉浅,绝知此事要躬行 &#x1f525; 个人主页&#xff1a;星云爱编程 &#x1f525; 所属专栏&#xff1a;javase &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 一、Object类 1.1equa…

GO--堆(have TODO)

堆 堆&#xff08;Heap&#xff09;是一种特殊的数据结构。它是一棵完全二叉树&#xff08;完全二叉树是指除了最后一层外&#xff0c;每一层上的节点数都是满的&#xff0c;并且最后一层的节点都集中在左边&#xff09;&#xff0c;结放在数组&#xff08;切片&#xff09;中…

java开发入门学习五-流程控制

流程控制语句 if&#xff0c; if...else&#xff0c; if..else if..else 与前端相同 略 switch case 与前端不同的是case不能使用表达式&#xff0c;使用表达式会报错 class TestSwitch {public static void main(String[] args) {// switch 表达式只能是特定的数据类型…

豆包MarsCode测评:编程效率再提升

豆包MarsCode测评&#xff1a;编程效率再提升 本文正在参与豆包MarsCode AI 编程体验家活动 随着人工智能技术的发展&#xff0c;编程的方式也在悄然发生变化。最近&#xff0c;豆包推出的 AI 编程工具 MarsCode 在开发者社区引发了不小的关注。这是一款支持多种主流编程语言…

FFmpeg 框架简介和文件解复用

文章目录 ffmpeg框架简介libavformat库libavcodec库libavdevice库 复用&#xff08;muxers&#xff09;和解复用&#xff08;demuxers&#xff09;容器格式FLVScript Tag Data结构&#xff08;脚本类型、帧类型&#xff09;Audio Tag Data结构&#xff08;音频Tag&#xff09;V…

Unity开发哪里下载安卓Android-NDK-r21d,外加Android Studio打包实验

NDK下载方法&#xff08;是r21d,不是r21e, 不是abc, 是d版本呢) google的东西&#xff0c;居然是完全开源的 真的不是很多公司能做到&#xff0c;和那种伪搜索引擎是不同的 到底什么时候google才会开始造车 不过风险很多&#xff0c;最好不要合资&#xff0c;风险更大 Andr…

leetcode-128.最长连续序列-day14

为什么我感觉上述代码时间复杂度接近O(2n), 虽然有while循环&#xff0c;但是前面有个if判断&#xff0c;能进入while循环的也不多&#xff0c;while循环就相当于两个for循环&#xff0c;但不是嵌套类型的&#xff1a; 变量作用域问题&#xff1a;

人工智能入门是先看西瓜书还是先看花书?

在人工智能入门时&#xff0c;关于先看《机器学习》&#xff08;西瓜书&#xff09;还是先看《深度学习》&#xff08;花书&#xff09;的问题&#xff0c;实际上取决于个人的学习目标和背景。 《机器学习》&#xff08;西瓜书&#xff09;由周志华教授撰写&#xff0c;是一本…

B 站数据库负责人赵月顺:助力海内外业务增长,百套 TiDB 的选型与运维实战

导读 B 站对 TiDB 的应用已相当广泛&#xff0c;被应用在了 包括视频观看、一键三连、发送弹幕、撰写评论、阅读漫画以及视频后端的存储等场景&#xff0c; 目前拥有近 100 套集群。 本文由 B 站数据库负责人赵月顺撰写&#xff0c; 详细介绍了 B 站面临业务增长选择 TiDB 的…

二九(vue2-05)、父子通信v-model、sync、ref、¥nextTick、自定义指令、具名插槽、作用域插槽、综合案例 - 商品列表

1. 进阶语法 1.1 v-model 简化代码 App.vue <template><!-- 11-src-下拉封装 --><div class"app"><!-- <BaseSelect :cityId"selectId" changeId"handleChangeId"></BaseSelect> --><!-- v-model 简化…

flask-admin+Flask-WTF 实现实现增删改查

背景&#xff1a; flask-adminflask-wtf在网上可以搜索到很多资料&#xff0c;但有价值的很少&#xff0c;或许是太简单&#xff0c;或者是很少人这么用&#xff0c;或者。。。&#xff0c;本文将作者近礼拜摸索到的一点经验分享出来&#xff0c;给自己做个记录。 材料&#…

Linux下基于最新稳定版ESP-IDF5.3.2开发esp32s3入门任务间的通讯-消息队列【入门四】

继续上一篇任务创建 【Linux下基于最新稳定版ESP-IDF5.3.2开发esp32s3入门任务间的通讯-信号量【入门三】-CSDN博客】 今天要实现消息队列进行任务的通讯 一、从上一篇信号量通讯demo拷贝一份重命名&#xff0c;还是之前的两个任务&#xff0c;重命名了。 xTaskCreatePinned…

workman服务端开发模式-应用开发-后端api推送修改二

需要修改两个地方&#xff0c;第一个是总控制里面的续token延时&#xff0c;第二个是操作日志记录 一、总控续token延时方法 在根目录下app文件夹下controller文件夹下Base.php中修改isLoginAuth方法&#xff0c;具体代码如下&#xff1a; <?php /*** 总控制* User: 龙哥…

ReactPress 1.6.0:重塑博客体验,引领内容创新

ReactPress 是一个基于Next.js的博客&CMS系统&#xff0c; Github项目地址&#xff1a;https://github.com/fecommunity/reactpress 欢迎Star。 体验地址&#xff1a;http://blog.gaoredu.com/ 今天&#xff0c;我们自豪地宣布ReactPress 1.6.0版本的正式发布&#xff0c;…