《Vue进阶教程》第十六课:深入完善响应式系统之单例模式
往期内容:
《Vue进阶教程》第五课:ref()函数详解(重点)
《Vue进阶教程》第六课:computed()函数详解(上)
《Vue进阶教程》第七课:computed()函数详解(下)
《Vue进阶教程》第八课:watch()函数的基本使用
《Vue进阶教程》第九课:watch()函数的高级使用
《Vue进阶教程》第十课:其它函数
《Vue进阶教程》第十一课:响应式系统介绍
《Vue进阶教程》第十二课:实现一对多
《Vue进阶教程》第十三课:实现依赖收集
《Vue进阶教程》第十四课:改进桶结构
《Vue进阶教程》第十五课:深入完善响应式系统之模块化
🤔思考
- 对于同一个源对象每次调用reactive返回的代理对象应该是一样的
- 对于一个已经代理过的对象再次代理应该返回的也应该是一样的
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>
相关文章:
《Vue进阶教程》第十六课:深入完善响应式系统之单例模式
往期内容: 《Vue进阶教程》第五课:ref()函数详解(重点) 《Vue进阶教程》第六课:computed()函数详解(上) 《Vue进阶教程》第七课:computed()函数详解(下) 《Vue进阶教程》第八课:watch()函数的基本使用 《Vue进阶教…...
C++ —— const修饰指针
C —— const修饰指针 常量指针(实际开发中用的很多)指针常量(了解即可)常指针常量(了解即可) 常量指针(实际开发中用的很多) 语法:const 数据类型 *变量名; 不能通过解…...
【学习笔记】数据结构(八)
动态存储管理 文章目录 动态存储管理8.1 概述8.2 可利用空间表及分配方法8.3 边界标识法8.3.1 可利用空间表的结构8.3.2 分配算法8.3.3 回收算法 8.4 伙伴系统8.4.1 可利用空间表的结构8.4.2 分配算法8.4.3 回收算法 8.5 无用单元收集 - 垃圾回收机制8.6 存储紧缩 - 内存碎片化…...
maven-resources-production:ratel-fast: java.lang.IndexOutOfBoundsException
Maven生产环境中遇到java.lang.IndexOutOfBoundsException的问题,尝试了重启电脑、重启IDEA等常规方法无效,最终通过直接重建工程解决了问题。 Rebuild Project 再启动OK...
建投数据与腾讯云数据库TDSQL完成产品兼容性互认证
近日,经与腾讯云联合测试,建投数据自主研发的人力资源信息管理系统V3.0、招聘管理系统V3.0、绩效管理系统V2.0、培训管理系统V3.0通过腾讯云数据库TDSQL的技术认证,符合腾讯企业标准的要求,产品兼容性良好,性能卓越。 …...
后端-添加购物车和查看购物车
...
【HarmonyOS NEXT】Web 组件的基础用法以及 H5 侧与原生侧的双向数据通讯
关键词:鸿蒙、ArkTs、Web组件、通讯、数据 官方文档Web组件用法介绍:文档中心 Web 组件加载沙箱中页面可参考我的另一篇文章:【HarmonyOS NEXT】 如何将rawfile中文件复制到沙箱中_鸿蒙rawfile 复制到沙箱-CSDN博客 目录 如何在鸿蒙应用中加…...
7-2 排序
输入一批未排序的数据,数量不超过30个,请使用选择法或者冒泡法对其排序,并按照规定的要求输出。 输入格式: 先输入待排序的整形数的个数;然后输入所有的待排序的数据。 输出格式: 在一行中按照由大到小的顺序输出排序好的数据…...
Java通过反射破坏单例模式
有个第三方工具类,不支持多例模式。但是又不能直接改第三方工具类的代码,因此可以通过反射破坏第三方工具类的单例。 第三方工具类反编译如下 可以看到构造函数进行了私有化,不允许外部new,只能通过newInstance进行实例化。并且…...
FFmpeg第一话:FFmpeg 简介与环境搭建
FFmpeg 探索之旅 一、FFmpeg 简介与环境搭建 二、FFmpeg 解码详解 第一话:FFmpeg 简介与环境搭建 FFmpeg 探索之旅一、前言二、FFmpeg 是什么?三、简单介绍其历史背景四、为什么用 C学习 FFmpeg?(一)高性能优势&#…...
C++并发编程: std::atomic对指针进行操作
std::atomic 对指针进行运算的用途 std::atomic 提供了一种在多线程环境中安全地操作指针的方法。这对于实现线程安全的指针管理、动态内存分配、链表操作等场景非常有用。通过使用std::atomic对指针进行运算,可以确保指针操作的原子性和多线程安全性。 常见用途 …...
工业大数据分析算法实战-day08
文章目录 day08模型评价聚类算法基于距离的聚类基于层次的聚类基于密度的聚类基于分布的聚类聚类结果的评价 day08 今天是第8天,昨日阐述了概率图模型和集成学习的分类,主要讲解了有向图和无向图,生成式模型和判断式模型,以及集成…...
在 C# 中实现的目录基础操作
前言 在开发应用程序过程中,对操作系统上的文件夹存储文件和子文件夹操作是常见的需求。.NET中的Directory类提供了处理文件目录的功能。本文介绍如何读取文件夹的属性、获取文件夹的大小及文件个数、创建文件夹、遍历文件夹中的所有文件、移动文件夹和删除文件夹等…...
Python 标识符是啥?
Python 的标识符就是我们写代码时用来给变量、函数、类等取名字的东西。 你写的 my_variable 是个标识符, 定义的 add_numbers 函数名也是个标识符, 甚至你写的 Cat 类名,也是标识符。 一句话总结:标识符就是代码里给“东西”起…...
WEB开发: 全栈工程师起步 - Python Flask +SQLite的管理系统实现
一、前言 罗马不是一天建成的。 每个全栈工程师都是从HELLO WORLD 起步的。 之前我们分别用NODE.JS 、ASP.NET Core 这两个框架实现过基于WebServer的全栈工程师入门教程。 今天我们用更简单的来实现: Python。 我们将用Python来实现一个学生管理应用࿰…...
将 Matplotlib 图形转换为 PIL 图像并返回
将 Matplotlib 图形转换为 PIL 图像并返回 前言完整代码Matplotlib 中 fig 和 ax 的关系示例: 问题分析常见错误及解决方案总结 前言 Matplotlib 是 Python 里最流行的图表展示库,PIL (Python Imaging Library)则是一个强大的图像处理库。在开发过程中&…...
F5中获取客户端ip地址(client ip)
当F5设备对其原始设置上的所有IP地址使用NAT时,连接到poo成员(nodes、backend servers)的出站连接将是NAT IP地址。 pool 成员(nodes、backend servers)将无法看到真实的客户端 ip地址,因为看到的是F5上的…...
点焊机器人维修-ABB-KUKA-FANUC-YASKAWA
在正式启用点焊机器人之前,一项至关重要的预备步骤便是进行焊枪的全面设置操作。以FANUC机器人为例,其焊枪的设置流程涵盖了多个关键环节,如焊枪运动方向的精确规划、焊枪规格的选择以及零点标定的细致执行等。这些设置均须严格依据实际所采用…...
springboot448教学辅助系统(论文+源码)_kaic
摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱,出错率高,信息安全性差&#x…...
网络地址转换(NAT)和端口映射
1. 网络地址转换(NAT) 1.1 NAT的应用场景 (1)应用场景:允许将私有IP地址映射到公网地址,以减缓IP地址空间的消耗 ①需要连接Internet,但主机没有公网IP地址 ②更换了一个新的ISP,需要重新组织网络时&…...
iClent3D for Cesium 实现无人机巡检飞行效果
作者:gaogy 1、背景 随着地理信息技术的发展,三维地球技术逐渐成为了许多领域中的核心工具,尤其是在城市规划、环境监测、航空航天以及军事领域。三维地图和场景的应用正在帮助人们更加直观地理解空间数据,提供更高效的决策支持。…...
低比特语言模型 是一种利用较少比特数进行语言建模的技术
Vanilla LLM: 基础的全精度语言模型,通常在较高比特数下运作 Vanilla LLM,或称为“基础的全精度语言模型”,是指使用标准的浮点数(通常是16位或32位)进行训练和推理的语言模型。这些模型依赖于经典的神经网络结构&…...
ES6中的map和set
Set ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。 Set本身是一个构造函数,用来生成 Set 数据结构。 以下代码 const s new Set();[2, 3, 5, 4, 5, 2, 2].forEach(x > s.add(x));for (let i of s…...
【WRF安装】WRF编译错误总结1:HDF5库包安装
目录 1 HDF5库包安装有误:HDF5 not set in environment. Will configure WRF for use without.HDF5的重新编译 错误原因1:提示 overflow 错误1. 检查系统是否缺少依赖库或工具2. 检查和更新编译器版本3. 检查 ./configure 报错信息4. 检查系统环境变量5.…...
MyBatis常见面试题总结
#{} 和 ${} 的区别是什么? 注:这道题是面试官面试我同事的。 答: ${}是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于原样文本替换,可以替换任意内容,比如${driver}…...
Qt生成随机数的方法
后台接口要求传个流水单号,流水号的格式是:“设备序列号”“设备MAC地址”“20位的随机数”。 具体的是: “设备序列号”:就是烧录工具写入设备的序列编号,这就不多说了,读出来就行; “设备MAC地…...
深入探索Vue.js中的v-if指令:条件渲染的高级技巧
深入探索Vue.js中的v-if指令:条件渲染的高级技巧 引言 在现代Web开发中,根据条件动态地渲染或移除DOM元素是一个常见的需求。Vue.js提供了一种简洁而强大的方法来实现这一目标,即通过v-if指令来根据表达式的值来控制元素的渲染。本文将详细…...
【记录50】uniapp安装uview插件,样式引入失败分析及解决
SassError: Undefined variable: "$u-border-color". 表示样式变量$u-border-color没定义,实际是定义的 首先确保安装了scss/sass 其次,根目录下 app.vue中是否全局引入 <style lang"scss">import /uni_modules/uview-ui/in…...
NTLMv2 离线爆破
攻击者(kali):192.168.72.162 受害者(administrator):192.168.72.163 因为 NTLM 身份验证是通过计算正确的挑战值得出的,所以如果我们能获取域用户的 NTLM 认证某一服务的 Net-NTLM v2 Hash …...
LabVIEW实现RFID通信
目录 1、RFID通信原理 2、硬件环境部署 3、程序架构 4、前面板设计 5、程序框图设计 6、测试验证 本专栏以LabVIEW为开发平台,讲解物联网通信组网原理与开发方法,覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合实际案例,展示如何利用LabVIEW和常用模块实现物联网系…...
【Three.js基础学习】31.Lights Shading
前言 关于灯光如何在着色器中应用! 下面将创建三个灯光 分别是点光源,环境光,方向光通过这几种光应用着色器显示对应阴影 学习灯光阴影,着色器的使用 添加三盏灯 点光,方向光,环境光 创建一个环境光 在现…...
Oracle Database 21c Express Edition数据库 和 Sqlplus客户端安装配置
目录 一. 前置条件二. Win10安装配置Oracle数据库2.1 数据库获取2.2 数据库安装2.3 数据库配置确认2.4 数据库访问 三. Win10配置Oracle数据库可对外访问3.1 打开文件和打印机共享3.2 开放1521端口 四. 端口与地址确认4.1 查看监听器的状态4.2 Win10查看1521端口是否被监听4.3 …...
IDEA搭建springboot demo
如下所示创建SpringBootTest18项目,我选的maven,创建完成项目后,maven会自动下载一些依赖库到maven的repository目录中。 创建的项目结构如下图所示 接下来在项目中加入Framework支持,右击项目,弹出的菜单如下图所示&a…...
SQLite Update 语句
SQLite Update 语句 SQLite 的 UPDATE 语句用于更新数据库表中的现有记录。使用 UPDATE 语句,您可以修改一个或多个列的值。本教程将详细介绍如何使用 SQLite UPDATE 语句,包括语法、示例以及一些最佳实践。 语法 SQLite UPDATE 语句的基本语法如下&a…...
node.js的简单示例
Node.js是一个基于Chrome V8引擎的JavaScript运行时环境,用于方便地构建快速、可扩展的网络应用。下面是一个简单的Node.js示例,它创建了一个简单的HTTP服务器,当访问服务器时,它会响应“Hello World” // 引入Node.js的HTTP模块…...
fpga系列 HDL:Quartus II 时序约束 静态时序分析 (STA) test.out.sdc的文件结构
test.out.sdc的文件结构 ## Generated SDC file "test.out.sdc"## Copyright (C) 1991-2013 Altera Corporation ## Your use of Altera Corporations design tools, logic functions ## and other software and tools, and its AMPP partner logic ## functions,…...
Restaurants WebAPI(一)—— clean architecture
文章目录 项目地址一、Restaurants.Domain 核心业务层1.1 Entities实体层1.2 Repositories 数据操作EF的接口二、Restaurants.Infrastructure 基础设施层2.1 Persistence 数据EF CORE配置2.2 Repositories 数据查询实现2.3 Extensions 服务注册三、Restaurants.Application用例…...
A Unified Framework for STAR-RIS Coefficients Optimization
文章目录 AbstractB. A Penalty-based Reformulation of (1) III. OPTIMIZING AUXILIARY VARIABLES φ \boldsymbol φ φ IN P1IV. A CASE STUDY OF P2 ON DOWNLINK STAR-RIS ASSISTED TRANSMISSION SYSTEMA. 优化 x , ρ , w , λ t x, \rho, \mathbf{w}, \lambda^t x,ρ,w…...
rebase ‘A‘ onto ‘master‘ 和 merge ‘master‘ into ‘A‘有什么区别
在Git版本控制系统中,rebase 和 merge 是两种不同的操作,用于合并分支。rebase A onto master 和 merge master into A 虽然最终目的都是将两个分支的更改合并在一起,但它们在处理方式和结果上有所不同。 rebase ‘A’ onto ‘master’ 含义…...
谷歌发布最新视频生成模型 Veo 2:视频生成AI新王牌
谷歌 在当今数字化快速发展的时代,人工智能视频生成技术正不断突破创新。就在12月17日,谷歌推出了一个新的视频模型 Veo 2 。 Veo 2 Veo 2 Veo 2 可以创建各种主题和风格的高质量视频。在谷歌官方由人工评估员判断中,Veo 2 与领先模型相比取得…...
2025erp系统开源免费进销存系统搭建教程/功能介绍/上线即可运营软件平台源码
系统介绍 基于ThinkPHP与LayUI构建的全方位进销存解决方案 本系统集成了采购、销售、零售、多仓库管理、财务管理等核心功能模块,旨在为企业提供一站式进销存管理体验。借助详尽的报表分析和灵活的设置选项,企业可实现精细化管理,提升运营效…...
基于Docker的Minio分布式集群实践
目录 1. 说明 2. 配置表 3. 步骤 3.1 放行服务端口 3.2 docker-compose 编排 4. 入口反向代理与负载均衡配置 4.1 api入口 4.2 管理入口 5. 用例 6. 参考 1. 说明 以多节点的Docker容器方式实现minio存储集群,并配以nginx反向代理及负载均衡作为访问入口。…...
解决node.js的req.body为空的问题
从昨晚一直在试,明明之前用的封装的axios发送请求给其他的后端(springboot)是可以的,但昨天用了新项目的后端(node.js)就不行。 之前用了代理,所以浏览器发送的post请求不会被拦截,…...
数据结构期末算法复习:树、查找、排序
一、树 1.二叉链-定义 typedef struct BiTNode{ ElemType data;//数据域 struct BiTNode *lchild ,*rchild;//左、右孩子指针 }BiTNode , *BiTree ;2.查找值为x的结点 BTNode FindNode(BTNode b,ElemType x) { BTNode *p;if (bNULL) return NULL;else if (…...
复习打卡Linux篇
目录 1. Linux常用操作命令 2. vim编辑器 3. 用户权限 4. Linux系统信息查看 1. Linux常用操作命令 基础操作: 命令说明history查看历史执行命令ls查看指定目录下内容ls -a查看所有文件 包括隐藏文件ls -l ll查看文件详细信息,包括权限类型时间大小…...
OpenAI API深度解析:参数、Token、计费与多种调用方式
随着人工智能技术的飞速发展,OpenAI API已成为许多开发者和企业的得力助手。本文将深入探讨OpenAI API的参数、Token、计费方式,以及如何通过Rest API(以Postman为例)、Java API调用、工具调用等方式实现与OpenAI的交互࿰…...
Centos7 部署ZLMediakit
1、拉取代码 #国内用户推荐从同步镜像网站gitee下载 git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit cd ZLMediaKit #千万不要忘记执行这句命令 git submodule update --init 2、安装编译器 sudo yum -y install gcc 3、安装cmake sudo yum -y install cmake 4…...
python:用 sklearn.metrics 评价 K-Means 聚类模型
sklearn 的 metrics 模块提供的聚类模型评价指标如下: ARI 评价法(兰德系数): adjusted_rand_score AMI 评价法(相互信息): adjusted_mutual_info_score V-measure 评分 : completeness_score FMI 评价法 : fowlkes_m…...
谁说C比C++快?
看到这个问题,我我得说:这事儿没有那么简单。 1. 先把最大的误区打破 "C永远比C快" —— 某位1990年代的程序员 这种说法就像"自行车永远比汽车省油"一样荒谬。我们来看个例子: // C风格 char* str (char*)malloc(100…...