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

Vue响应式数据的实现原理(手写副作用函数的存储和执行过程)

1.命令式和声明式框架

命令式框架关注过程

声明式框架关注结果(底层对命令式的DOM获取和修改进行了封装)

2.vue2 Object.defineProperty()双向绑定的实现

<body><div id="app"><input type="text" /><h1></h1><button>按钮</button> </div>
</body>
<script>//   vue2实现双绑const input = document.getElementsByTagName('input')[0]const h1 = document.getElementsByTagName('h1')[0]const btn = document.getElementsByTagName('button')[0]let data = { text: '' }// input框输入数据,h1数据和text一致;实现点击按钮时,h1 标签数据和input框数据同时更改Object.defineProperty(data, 'text', {get() {return data['text'];},set(value) {// 获取到值后将h1后的内容设置为texth1.innerText = value;input.value = value;return true;}});input.oninput = function (e) {data.text = e.target.value;}btn.onclick = function () {data.text = "你好"}
</script>

3.同样页面Vu3实现 new Proxy()

        // vue3实现双绑const input = document.getElementsByTagName('input')[0]const h1 = document.getElementsByTagName('h1')[0]const btn = document.getElementsByTagName('button')[0]let data = { text: '' }let obj = new Proxy(data, {get(target, property) {return target[property]},set(target, property, value) {h1.innerText = value;input.value = value;return true;}})input.oninput = function (e) {obj.text = e.target.value;}btn.onclick = function () {obj.text = "你好"}

4.响应式数据的基本实现

响应式数据的关键是拦截对象属性的设置和读取操作

const data = { text: '' }
function effect () {document.body.innerText = data.text
}

5. vue2与vue3响应式数据实现区别

  • vue2的实现:当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用Object.defineProperty() 把这些 property 全部转为 getter/setter。
  • vue3的实现:当我们从一个组件的 data 函数中返回一个普通的 JavaScript 对象时,Vue 会将该对象包裹在一个带有 get 和 set 处理程序的Proxy 中

6.vue3 proxy的简单实现响应式数据拦截

// 初始数据
const data = { text: '' }
// 存储副作用函数的桶
const bucket = new Set()
// 对数据进行代理
const obj = new Proxy(data, {get(target, key) {bucket.add(effect)return target[key]},set(target, key, newVal) {target[key] = newValbucket.forEach(fn => fn())return true}
})function effect () {document.body.innerText = obj.text
}effect()setTimeout(() => {obj.text = '你好'
}, 1000)

7.简单实现中出现的问题

思考一下 这一段代码的问题。

  • 1. 副作用函数的名称被写死
  • 2. 没有建立副作用函数和目标字段之前明确的关系

副作用函数的名称被写死——解决:

名称写死问题:真实情况下副作用函数不可能只有一个,那如果有多个函数时,每个函数执行都会调用一个副作用函数。比如,设置obj.a = 2,同样会调用set方法中的bucket.forEach(fn => fn())方法。

通用一个副作用作用函数,将执行DOM修改的函数以闭包形式(回调函数)传入副作用函数,这样每次返回的都不是同一个函数

let activeEffectfunction effect(fn) {activeEffect = fnfn()
}effect(() => {document.body.innerText = obj.text
})const obj = new Proxy(data, {get(target, key) {if (activeEffect) {bucket.add(activeEffect)}return target[key]},set(target, key, newVal) {target[key] = newValbucket.forEach(fn => fn())return true}
})

没有建立副作用函数和目标字段之前明确的关系——解决:

以上代码对每个属性都绑定了同一个副作用函数,实际上真实需要的时,修改text时,调用他自己的函数,修改a时又调用a对应的函数

解决:

  1. 使用Map键值数据结构(分两层)进行存储副作用函数,将每个数据对象对应一个map的键;
  2. 一个数据对象下不同属性又存储到一个Map数据下,这个map的值则存储的副作用函数(Set形式存)
  3. 使用时通过获取对象的map(数据对象)下的对应属性key(每个对象的key)的值的set数据(针对每个属性操作的副作用函数)并进行遍历执行即可

const obj = new Proxy(data, {get(target, key) {console.log(activeEffect, 'activeEffect')// 没有副作用函数,容错处理 直接returnif (!activeEffect) return target[key]// 判断桶中是否已经存在key与target的关联关系let depsMap = bucket.get(target)// 创建一个新的Map结构与 target 关联if (!depsMap) {bucket.set(target, (depsMap = new Map()))}// 判断当前Map数据中是否存在key与effect的关联关系let deps = depsMap.get(key)// 不存在的话 则新建一个Set 与 key关联if (!deps) {depsMap.set(key, (deps = new Set()))}// 最后将当前激活的副作用函数添加到bucket中deps.add(activeEffect)return target[key]},set (target, key, newVal) {target[key] = newVal// 获取bucket下对应的数据const depsMap = bucket.get(target)if (!depsMap) return// 根据key 获取副作用的执行函数const effects = depsMap.get(key)// 执行副作用函数effects && effects.forEach(fn => fn())}
})effect(() => {document.body.innerText = obj.text
})
effect(() => {document.title = obj.a
})

8.完全实现——我的测试

 const data = { text: '这是obj.title', a:'vue响应式数据的实现原理' }// 存储副作用函数的桶const bucket = new Map()// 存储副作用函数的变量let activeEffect;// 对数据进行代理const obj = new Proxy(data, {get(target, key) {// 设置副作用函数到map数据的桶中// 判断不存在activeEffect直接返回if (!activeEffect) return target[key];// 存在activeEffect时将副作用函数设置到桶中let targetMap = bucket.get(target); //target对象存在才能判断key是否存在(targetMap既是bucket的值targetMap = new Map()又是keyMap的键的定义)if (!targetMap) {bucket.set(target, (targetMap = new Map()));}let keyMap = targetMap.get(key); // keyMap既是targetMap的值又是if (!keyMap) {targetMap.set(key, (keyMap = new Set()))}keyMap.add(activeEffect); //副作用函数最终是存在Set结构里面的return target[key]},set(target, key, newVal) {target[key] = newVal;//获取桶里面的对象Map的key的map集合的值,即所有的副作用函数进行循环执行let targetMap = bucket.get(target);if (!targetMap) return;let effects = targetMap.get(key);effects.forEach(fn => fn())return true}})function effect(fn) {// 将函数赋值给activeEffect,数据劫持到activeEffect有值时,会将其设置到bucket存储副作用的桶中,当拦截到数据更改时再获取对应函数并执行activeEffect = fn;fn();}// 副作用函数执行一次effect(() => {document.body.innerText = obj.text})effect(() => {document.title = obj.a})

相关文章:

Vue响应式数据的实现原理(手写副作用函数的存储和执行过程)

1.命令式和声明式框架 命令式框架关注过程 声明式框架关注结果&#xff08;底层对命令式的DOM获取和修改进行了封装&#xff09; 2.vue2 Object.defineProperty()双向绑定的实现 <body><div id"app"><input type"text" /><h1>…...

内核进程的调度与进程切换

进程被创建到了链表中&#xff0c;如何再进行进一步的调用和调用&#xff1f; 进程调度 void schedule(void)&#xff1b; 进程调度 switch_to(next); 进程切换函数 void schedule(void) {int i,next,c;struct task_struct ** p;/* check alarm, wake up any i…...

docker-rabbitmq 安装依赖

出现的问题如下: channel error; protocol method: #method(reply-code404, reply-textNOT_FOUND - no channel error&#xff1b; protocol method: #method&#xff1c;channel.close&#xff1e;(reply-code404, reply-textNOT_FOUND - no 查看rabbitmq 客户端是否存在如…...

(1)(1.9) HC-SR04声纳

文章目录 前言 1 连接到自动驾驶仪 2 参数说明 前言 HC-SR04 声纳是一种价格低廉但量程很短&#xff08;最远只有 2m&#xff09;的测距仪&#xff0c;主要设计用于室内&#xff0c;但也成功地在室外的 Copter 上使用过。极短的测距范围使其用途有限。 &#xff01;Warning…...

06 MIT线性代数-列空间和零空间 Column space Nullspace

1. Vector space Vector space requirements vw and c v are in the space, all combs c v d w are in the space 但是“子空间”和“子集”的概念有区别&#xff0c;所有元素都在原空间之内就可称之为子集&#xff0c;但是要满足对线性运算封闭的子集才能成为子空间 中 2 …...

【每日一题Day360】LC1465切割后面积最大的蛋糕 | 贪心

切割后面积最大的蛋糕【LC1465】 矩形蛋糕的高度为 h 且宽度为 w&#xff0c;给你两个整数数组 horizontalCuts 和 verticalCuts&#xff0c;其中&#xff1a; horizontalCuts[i] 是从矩形蛋糕顶部到第 i 个水平切口的距离verticalCuts[j] 是从矩形蛋糕的左侧到第 j 个竖直切口…...

中国地名信息库

地名是社会基本公共信息&#xff0c;是历史文化的重要载体。 2014年至2018年&#xff0c;国家启动实施并完成了第二次全国地名普查工作&#xff0c;全国共计采集地名1320多万条&#xff0c;修测标绘地名图2.4万多幅&#xff0c;新设更新地名标志68万多块&#xff0c;普遍建立了…...

网络时代下的声音之路:如何在中央新闻媒体发布网评稿

在当今数字时代&#xff0c;信息传播已经变得更加便捷和广泛。各大中央新闻媒体平台为民众提供了一个发布观点、表达意见的平台。在这个背景下&#xff0c;撰写并发布网评稿成为了一种重要的社会参与方式。根据媒介易软文发稿平台的总结&#xff0c;下面是探讨如何在各大中央新…...

Selenium中WebDriver最新Chrome驱动安装教程

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…...

云原生Docker数据管理

目录 Docker的数据管理 数据卷 数据卷容器 容器互联 容器中管理数据主要有两种方式&#xff1a; 数据卷&#xff08;Data Volumes&#xff09;数据卷容器&#xff08;Data Volume Dontainers&#xff09; Docker的数据管理 数据卷 数据卷是一个供容器使用的特殊目录&a…...

endnote设置

问题1&#xff1a;参考文献的tab太长 首先要在endnote里面这样设置&#xff0c;file->output->edit "XXX" 保存之后&#xff0c;在word更新目录。 在word里面设置悬挂缩进 结果&#xff1a; Endnote参考编号与参考文献距离太远怎么调整 endnote 文献对齐方式…...

计算机网络整理-简称缩写【期末复习|考研复习】

文章目录 前言一、物理层1.1 FDM频分复用 Frequency-division multiplexing1.2 TDM时分复用 Time-division multiplexing1.3 WDM波分复用 Wavelength Division Multiplexing1.4 Hub 集线器1.5 FSK频移键控 Frequency-shift keying 二、数据链路层2.1 GBN回退N步协议 Go Back N …...

Flink Hive Catalog操作案例

在此对Flink读写Hive表操作进行逐步记录&#xff0c;需要指出的是&#xff0c;其中操作Hive分区表和非分区表的DDL有所不同&#xff0c;以下分别记录。 基础环境 Hive-3.1.3 Flink-1.17.1 基本操作与准备 1、上传依赖jar包到flink/lib目录下 cp flink-sql-connector-hive-…...

NSSCTF做题第9页(3)

[GKCTF 2020]CheckIN 代码审计 这段代码定义了一个名为ClassName的类&#xff0c;并在脚本的最后创建了一个ClassName类的实例。 在ClassName类的构造函数中&#xff0c;首先通过调用$this->x()方法获取了请求参数$_REQUEST中的值&#xff0c;并将其赋值给$this->code属性…...

从瀑布模式到水母模式:ChatGPT如何赋能软件研发全流程【文末送书五本】

从瀑布模式到水母模式&#xff1a;ChatGPT如何赋能软件研发全流程 前言内容简介购买链接作者简介专家推荐读者对象参与方式往期赠书回 &#x1f3d8;️&#x1f3d8;️个人简介&#xff1a;以山河作礼。 &#x1f396;️&#x1f396;️:Python领域新星创作者&#xff0c;CSDN实…...

设置使用LibreOffice作为默认程序打开word、excel等文档

以win7为例。打开控制面板&#xff0c;点击程序&#xff1a; 点击“设置默认程序”&#xff1a; 左侧选中LibreOffice&#xff0c;然后在右下方点击“选择此程序的默认值”&#xff1a; 然后根据自己的需要勾选就行了&#xff1a;...

创新领航 | 竹云参编《基于区块链的数据资产评估实施指南》正式发布!

10月25日&#xff0c;由深圳数宝数据服务股份有限公司和深圳职业技术大学提出&#xff0c;中国科学院深圳先进技术研究院、中国电子技术标准化研究院、中国&#xff08;天津&#xff09;自由贸易试验区政策与产业创新发展局、网络空间治理与数字经济法治&#xff08;长三角&…...

【Docker】Linux网桥连接多个命名空间

veth实现了点对点的虚拟连接&#xff0c;可以通过veth连接两个namespace&#xff0c;如果我们需要将3个或者多个namespace接入同一个二层网络时&#xff0c;就不能只使用veth了。 在物理网络中&#xff0c;如果需要连接多个主机&#xff0c;我们会使用bridge&#xff08;网桥&…...

ES6新特性:let关键字详解

文章目录 1 声明提升2 作用域3 重复声明 在JavaScript中&#xff0c;let 和 var 都是声明变量的关键字&#xff0c;但在用法和作用域方面有一些区别。 let 是ES6引入的新的声明变量的关键字&#xff0c;它与 var 相比&#xff0c;更加严格&#xff0c;语法更加规范&#xff0c…...

鸿运主动安全监控云平台任意文件下载漏洞复现 [附POC]

文章目录 鸿运主动安全监控云平台任意文件下载漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 鸿运主动安全监控云平台任意文件下载漏洞复现 [附POC] 0x01 前言 免责声明&#xff1a;请勿利用文章内的相关技术…...

AI辅助开发:描述需求即可自动生成流程图,比手动visio下载更智能

最近在做一个用户系统的设计&#xff0c;需要画登录注册的流程图。以前用Visio这类工具时&#xff0c;经常要手动拖拽各种图形、调整连线&#xff0c;特别费时间。这次尝试了用AI辅助生成&#xff0c;发现效率提升太多了&#xff01; 需求解析环节 我把需求描述成一段自然语言&…...

Linux shell之for in的使用及说明

一、语法 1 2 3 4 for 变量名 in 列表 do 程序段(command) done 注意1&#xff1a;是变量名而不是$变量&#xff01; 注意2&#xff1a;列表可以做文章&#xff01; 二、应用 第一类&#xff1a;数字性循环-->seq在in后面的应用 1 2 3 4 5 6 #!/bin/bash …...

Adobe-GenP: 实现Adobe CC全版本破解的自动化补丁解决方案

Adobe-GenP: 实现Adobe CC全版本破解的自动化补丁解决方案 【免费下载链接】Adobe-GenP Adobe CC 2019/2020/2021/2022/2023 GenP Universal Patch 3.0 项目地址: https://gitcode.com/gh_mirrors/ad/Adobe-GenP Adobe Creative Cloud系列软件作为创意行业的标准工具&am…...

MelonLoader Cpp2IL组件加载故障解决方案:从排查到优化

MelonLoader Cpp2IL组件加载故障解决方案&#xff1a;从排查到优化 【免费下载链接】MelonLoader The Worlds First Universal Mod Loader for Unity Games compatible with both Il2Cpp and Mono 项目地址: https://gitcode.com/gh_mirrors/me/MelonLoader 问题现象&am…...

DCM模式反激电源各参数逻辑关系

在DCM模式下&#xff0c;变压器本质上是一个“能量存储-释放”的中间体&#xff0c;初级存储的能量必须在每个周期完全释放给次级。1. 变压器初级电感量&#xff08;Lp&#xff09;与最大占空比&#xff08;Dmax​&#xff09;逻辑关系&#xff1a; 在输入电压&#xff08;Vin&…...

手机相册端侧文本搜图方案调研

手机相册端侧文本搜图方案调研 调研日期:2026-04-02(UTC) 目标场景:手机相册中存在大量图片,需要支持基于自然语言的本地搜图;希望模型与系统架构可在骁龙平台端侧执行,并具备后续接入 tag/caption 与 rerank 的可扩展性。 一、结论摘要 已有现成开源例子,最接近目标场…...

SMUDebugTool:深度控制AMD Ryzen硬件参数的系统调试解决方案

SMUDebugTool&#xff1a;深度控制AMD Ryzen硬件参数的系统调试解决方案 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: http…...

Claude颠覆AI编程

&#x1f680; Claude 4 正式发布&#xff01;Anthropic 这次真的要颠覆 AI 编程了 今天&#xff0c;AI 领域迎来核弹级更新——Anthropic 正式发布 Claude 4 系列模型&#xff01;免费可用、7 小时自主编程&#xff0c;开发者直呼"生产力革命来了"&#xff01; 一、…...

终极指南:Android AdvancedRecyclerView 低版本兼容处理与 API 14+适配方案

终极指南&#xff1a;Android AdvancedRecyclerView 低版本兼容处理与 API 14适配方案 【免费下载链接】android-advancedrecyclerview RecyclerView extension library which provides advanced features. (ex. Googles Inbox app like swiping, Play Music app like drag and…...

Cisco Catalyst 8000 IOS XE 17.18.2 ED - 思科 Catalyst 8000 边缘平台系列 IOS XE 系统软件

Cisco Catalyst 8000 Series Edge Platforms, IOS XE Release 17.18.2 ED 思科 Catalyst 8000 边缘平台系列 IOS XE 系统软件 请访问原文链接&#xff1a;https://sysin.org/blog/cisco-catalyst-8000/ 查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff…...