手动实现 Vue 3的简易双向数据绑定(模仿源码)
Vue 3 带来了许多令人兴奋的新特性和改进,其中之一就是其双向数据绑定的实现方式。与 Vue 2 使用 Object.defineProperty
不同,Vue 3 利用了 JavaScript 的 Proxy
特性来创建响应式数据。在这篇博客中,我们将探讨 Vue 3 中双向数据绑定的基础原理,并尝试手动实现一个简化版的这一机制。
核心概念
Vue 3 的双向绑定依赖于两个核心概念:响应式代理(Reactive Proxy) 和 Effect 依赖收集系统。
响应式代理
Vue 3 使用 Proxy
对象来创建响应式数据。这允许框架在不改变对象本身结构的情况下,拦截并跟踪属性的访问和修改。
Effect 依赖收集系统
Effect
依赖收集系统用于自动追踪响应式数据的使用情况,并在数据变化时重新执行副作用(如渲染函数)。
实现步骤
以下是手动实现 Vue 3 双向绑定的简化步骤:
1. 创建 reactive
函数
这个函数用于将普通对象转换为响应式代理。
function reactive(target) {// 使用 Proxy 对象创建响应式数据return new Proxy(target, {// 拦截对象属性的读取get(target, key, receiver) {console.log(`访问了属性:${key}`);// 使用 Reflect.get 保证 this 指向正确return Reflect.get(target, key, receiver);},// 拦截对象属性的设置set(target, key, value, receiver) {console.log(`设置了属性:${key},新值为:${value}`);// 使用 Reflect.set 设置属性值Reflect.set(target, key, value, receiver);// 此处省略了依赖通知逻辑,实际 Vue 3 中会触发更新return true;}});
}
2. 定义 effect
函数
effect
函数用于注册副作用函数,并在响应式数据变化时执行这些函数。
let activeEffect = null;function effect(fn) {// 设置当前活动的副作用函数activeEffect = fn;// 立即执行函数,用于依赖收集fn();// 重置当前活动的副作用函数activeEffect = null;
}
3. 改进 reactive
以收集依赖
现在我们需要修改 reactive
函数,以支持依赖收集和派发更新。
const targetMap = new WeakMap();function track(target, key) {// 如果没有活动的副作用函数,直接返回if (!activeEffect) return;// 从 targetMap 中获取当前对象的所有依赖let depsMap = targetMap.get(target);if (!depsMap) {// 如果没有,就创建新的 Map 并存入 targetMaptargetMap.set(target, (depsMap = new Map()));}// 获取当前属性的所有依赖let dep = depsMap.get(key);if (!dep) {// 如果没有,就创建新的 Set 并存入 depsMapdepsMap.set(key, (dep = new Set()));}// 将当前活动的副作用函数添加到依赖集合中dep.add(activeEffect);
}function trigger(target, key) {// 从 targetMap 中获取对象的所有依赖const depsMap = targetMap.get(target);if (!depsMap) return;// 获取当前属性的所有依赖let dep = depsMap.get(key);if (dep) {// 对于每一个依赖(即副作用函数),执行它dep.forEach(effect => effect());}
}// 更新 reactive 函数
function reactive(target) {return new Proxy(target, {// 拦截属性读取get(target, key, receiver) {// 收集依赖track(target, key);return Reflect.get(target, key, receiver);},// 拦截属性设置set(target, key, value, receiver) {// 设置属性值Reflect.set(target, key, value, receiver);// 触发更新trigger(target, key);return true;}});
}
4. 测试实例
创建一个响应式对象,并观察它的变化。
const state = reactive({ count: 0 });// 注册一个副作用函数来模拟渲染逻辑
effect(() => {console.log(`count is now: ${state.count}`);
});// 更改响应式数据的属性,触发副作用函数
state.count++;
完整代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vue 3 简易双向数据绑定测试</title>
</head>
<body><h1>Vue 3 简易双向数据绑定测试</h1><div id="app"><p>Count: <span id="count">0</span></p><button id="increment">Increment</button></div><script>// reactive 函数function reactive(target) {const handler = {get(target, key, receiver) {track(target, key);return Reflect.get(target, key, receiver);},set(target, key, value, receiver) {Reflect.set(target, key, value, receiver);trigger(target, key);return true;}};return new Proxy(target, handler);}let activeEffect = null;function effect(fn) {activeEffect = fn;fn();activeEffect = null;}const targetMap = new WeakMap();function track(target, key) {if (!activeEffect) return;let depsMap = targetMap.get(target);if (!depsMap) {targetMap.set(target, (depsMap = new Map()));}let dep = depsMap.get(key);if (!dep) {depsMap.set(key, (dep = new Set()));}dep.add(activeEffect);}function trigger(target, key) {const depsMap = targetMap.get(target);if (!depsMap) return;let dep = depsMap.get(key);if (dep) {dep.forEach(effect => effect());}}// 测试代码const state = reactive({ count: 0 });effect(() => {document.getElementById('count').innerText = state.count;});document.getElementById('increment').addEventListener('click', () => {state.count++;});</script>
</body>
</html>
在这个文件中,我们创建了一个按钮,每当它被点击时,就会增加 state.count
的值。这个值的变化会触发绑定的副作用函数,从而更新显示在页面上的计数结论
小结
以上代码展示了一个 Vue 3 中双向数据绑定的简化实现。通过 Proxy
和动态依赖收集系统,我们可以创建一个灵活且强大的响应式系统。这种实现方式使得 Vue 3 在处理复杂应用时更为高效和灵活。
相关文章:
手动实现 Vue 3的简易双向数据绑定(模仿源码)
Vue 3 带来了许多令人兴奋的新特性和改进,其中之一就是其双向数据绑定的实现方式。与 Vue 2 使用 Object.defineProperty 不同,Vue 3 利用了 JavaScript 的 Proxy 特性来创建响应式数据。在这篇博客中,我们将探讨 Vue 3 中双向数据绑定的基础…...
LVS最终奥义之DR直接路由模式
1 LVS-DR(直接路由模式) 1.1 LVS-DR模式工作过程 1.客户端通过VIP将访问请求报文(源IP为客户端IP,目标IP为VIP)发送到调度器 2.调度器通过调度算法选择最适合的节点服务器并重新封装数据报文(将源mac地址改为调度器的mac地址&am…...
t-SNE高维数据可视化实例
t-SNE:高维数据分布可视化 实例1:自动生成一个S形状的三维曲线 实例1结果: 实例1完整代码: import matplotlib.pyplot as plt from sklearn import manifold, datasets """对S型曲线数据的降维和可视化"&q…...
配置应用到k8s
配置应用到k8s,前置条件是安装了Docker,Minikube,kubectl 应用已经通过Docker生成本地镜像文件 1,创建godemo-deployment.yaml apiVersion: apps/v1kind: Deploymentmetadata:name: godemo-deploymentspec:replicas: 3 #启动三个…...
(四)STM32 操作 GPIO 点亮 LED灯 / GPIO工作模式
目录 1. STM32 工程模板中的工程目录介绍 2. GPIO 简介 3. GPIO 框图剖析 1)保护二极管及上、下拉电阻 2) P-MOS 管和 N-MOS 管 3)输出数据寄存器 3.1)ODR 端口输出数据寄存器 3.2)BSRR 端口位设置/清除寄存器 4&a…...
你知道跨站脚本攻击吗?一篇带你了解什么叫做XSS
1.XSS简介 (1)XSS简介 XSS作为OWASP TOP 10之一。 XSS中文叫做跨站脚本攻击(Cross-site scripting),本名应该缩写为CSS,但是由于CSS(Cascading Style Sheets,层叠样式脚本&#x…...
JVM入门
JVM概述 JVM位置 JVM体系结构 注意:栈中一定不存在垃圾,栈中数据用完一个弹出一个,总结来说,栈区、本地方法栈、程序计数器这三块必定不存在垃圾。JVM调优主要是针对方法区、堆(99%)进行调优。 常用的第三…...
Cmake基础(5)
这篇文章主要描述如何使用cmake构建一个库工程 文章目录 add_libraryinstall 库工程的代码:头文件和源文件 #ifndef ADD_H #define ADD_H#ifdef _WIN32 #ifdef MYMATH_EXPORTS #define MYMATH_API __declspec(dllexport) #else #define MYMATH_API __declspec(dll…...
Rabbitmq 死信取消超时订单
本文使用的版本 otp_win64_25.0rabbitmq-server-3.11.26rabbitmq插件 rabbitmq_delayed_message_exchange-3.11.1 pom.xml文件 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId> …...
C语言—每日选择题—Day55
指针相关博客 打响指针的第一枪:指针家族-CSDN博客 深入理解:指针变量的解引用 与 加法运算-CSDN博客 第一题 1. 若有如下定义,则 p1&m;p2p1; 是正确赋值语句.说法是否正确? int *p1; int *p2; int m …...
软件测试岗位的简历怎么写?项目怎么包装
已经帮大家打包好了包装好的简历模板,大家可以直接进行套用,详情请望下看 自动化测试相关教程推荐: 2023最新自动化测试自学教程新手小白26天入门最详细教程,目前已有300多人通过学习这套教程入职大厂!!_哔哩哔哩_bili…...
服务器解析漏洞是什么?攻击检测及修复
服务器解析漏洞(Server-side Include Vulnerability,SSI漏洞)是一种安全漏洞,通常出现在支持服务器端包含(SSI)功能的Web服务器上。SSI是一种在Web页面中嵌入动态内容的技术,允许开发人员将外部…...
HTML---CSS美化网页元素
文章目录 前言一、pandas是什么?二、使用步骤 1.引入库2.读入数据总结 一.div 标签: <div>是HTML中的一个常用标签,用于定义HTML文档中的一个区块(或一个容器)。它可以包含其他HTML元素,如文本、图像…...
【Docker】基础篇
文章目录 Docker为什么出现容器和虚拟机关于虚拟机关于Docker二者区别: Docker的基本组成相关概念-镜像,容器,仓库安装Docker卸载docker阿里云镜像加速docker run的原理**为什么容器比虚拟机快**Docker的常用命令1.帮助命令2.镜像相关命令3.容…...
Potplayer播放器远程访问群晖WebDav本地资源【内网穿透】
文章目录 本教程解决的问题是:按照本教程方法操作后,达到的效果是:1 使用环境要求:2 配置webdav3 测试局域网使用potplayer访问webdav3 内网穿透,映射至公网4 使用固定地址在potplayer访问webdav 国内流媒体平台的内容…...
【神经网络】imshow展示图片报错
文章目录 代码示例报错信息报错原因解决方法其他问题 代码示例 plt.imshow(np.squeeze(images[0]))报错信息 Invalid shape (3, 60, 90) for image data报错原因 格式错误,输入具有RGB值的图像,输入三维数组参数的格式应该是(高度…...
【C++】对象特性:无参有参构造函数,拷贝构造函数,析构函数
目录 对象的初始化和清理1.1 构造函数和析构函数1.2 构造函数的分类及调用1.3 拷贝构造函数调用时机1.4 构造函数调用规则1.5 深拷贝与浅拷贝 对象的初始化和清理 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据保证安全。…...
【算法与数据结构】1005、LeetCode K 次取反后最大化的数组和
文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:本题允许某个下标的数字多次翻转,因此思路比较简单。首先,我们要求最大和&…...
作业--day34
使用select完成TCP并发服务器和客户端 server.c #include <myhead.h>#define PORT 8888 #define IP "192.168.125.137"int main(int argc, const char *argv[]) {int sfd socket(AF_INET, SOCK_STREAM, 0);if(sfd -1){perror("socket error");re…...
车辆违规开启远光灯检测系统:融合YOLO-MS改进YOLOv8
1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义 随着社会的不断发展和交通工具的普及,车辆违规行为成为了一个严重的问题。其中,车辆违规开启远光灯是一种常见的违规行为,给其…...
爬虫工作量由小到大的思维转变---<第十三章 Scrapy之pipelines分离的思考>
前言: 收到留言: "我的爬取的数据处理有点大,scrapy抓网页挺快,处理数据慢了!" -----针对这位粉丝留言,我只想说:你那培训班老师可能给你漏了课程! 大概你们上课讲的案例属于demo,他教了你一些基本操作,但他没有对相关业务对你讲透! 你研究一下pipelines,或者看我现…...
【Unity】运行时创建曲线(贝塞尔的运用)
[Unity]运行时创建线(贝塞尔的运用) 1. 实现的目标 在运行状态下创建一条可以使用贝塞尔方法实时编辑的网格曲线。 2. 原理介绍 2.1 曲线的创建 unity建立网格曲线可以参考Unity程序化网格体的实现方法。主要分为顶点,三角面,…...
基于DSP的IIR数字滤波器(论文+源码)
1.系统设计 在本次基于DSP的IIR数字低通滤波计中,拟以TMS320F28335来作为系统的主控制器,通过ADC0832模数转换芯片来对输入信号进行采集;通过TLC5615来将低通滤波后的信号进行输出;同时结合MATLAB仿真软件,对设计的II…...
Django(一)
1.web框架底层 1.1 网络通信 注意:局域网 个人一般写程序,想要让别人访问:阿里云、腾讯云。 去云平台租服务器(含公网IP)程序放在云服务器 先以局域网为例 我的电脑【服务端】 import socket# 1.监听本机的IP和…...
微信小程序如何利用createIntersectionObserver实现图片懒加载
微信小程序如何利用createIntersectionObserver实现图片懒加载 节点布局相交状态 API 可用于监听两个或多个组件节点在布局位置上的相交状态。这一组API常常可以用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见。 节点布局相交状态 API中有一个 wx.createInter…...
七:爬虫-数据解析之正则表达式
七:正则表达式概述 正则表达式,又称规则表达式,(Regular Expression,在代码中常简写为regex、regexp或RE),是一种文本模式,包括普通字符(例如,a 到 z 之间的字母…...
云原生之深入解析亿级流量架构之服务限流思路与方法
一、限流思路 ① 熔断 系统在设计之初就把熔断措施考虑进去,当系统出现问题时,如果短时间内无法修复,系统要自动做出判断,开启熔断开关,拒绝流量访问,避免大流量对后端的过载请求。系统也应该能够动态监测…...
【Python炫酷系列】祝考研的友友们金榜题名吖(完整代码)
文章目录 环境需求完整代码详细分析系列文章环境需求 python3.11.4及以上版本PyCharm Community Edition 2023.2.5pyinstaller6.2.0(可选,这个库用于打包,使程序没有python环境也可以运行,如果想发给好朋友的话需要这个库哦~)【注】 python环境搭建请见:https://want595.…...
KL散度、CrossEntropy详解
文章目录 0. 概述1. 信息量1.1 定义1.2 性质1.3 例子2. 熵 Entropy2.1 定义2.2 公式2.3 例子3. 交叉熵 Cross Entropy3.1 定义3.2 公式3.3 例子4. KL 散度(相对熵)4.1 公式...
【算法】红黑树
一、红黑树介绍 红黑树是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。 红黑树是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)。后来&am…...
怎么建设境外网站/免费的网站推广在线推广
1065 单身狗 “单身狗”是中文对于单身人士的一种爱称。本题请你从上万人的大型派对中找出落单的客人,以便给予特殊关爱。 输入格式: 输入第一行给出一个正整数 N(≤ 50 000),是已知夫妻/伴侣的对数;随后 N…...
网站怎么上传ftp/做网络销售如何找客户
在用dbca命令进去删除数据库实例的时候,如下图: 可以看到删除数据库这一栏为灰色,根本删除不了! 笔者在百度上搜索到了这篇文章:点我 上面是这么解决的: 环境 windows7 ,10g 10.2.0.4 找到 bin下 dbca.…...
重庆网站建设注意事项/快排seo
接上一篇 WPF多进程UI探索(Like Chrome) 找到了相对较靠谱的跨进程传递WPFUI的方法,本篇将对WPF多进程UI框架进行设计。 功能性需求 一个主进程作为宿主,承载多个子进程的UI每个子进程相互独立,互不影响主进程和子进程…...
weui.css做网站/学生个人网页制作成品代码
液晶画面模式用 播放用(基本信息显示) 取景器模式用 在自动模式或场景选择模式下 在P/A/S/M/扫描全景模式下 1.上部图示 照相模式 存储号码 场景识别图标 存储卡/上传状态 100 剩余可拍摄影像数 静止影像的纵横比 ILCE-7: 24M&#x…...
wordpress评论去除/seo是什么缩写
转帖来的,oracle有层级插叙你的专门函数,mysql的木有,但是可以自己做。在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT BY 我们可以方便的查了所有当前节点下的所有子节点。但很遗憾,在MySQL的目前版本中还没有对应…...
做网站这个工作怎么样/seo是什么技术
需求描述 在表单中,可能部分表单项需封装成自定义组件,如何在表单提交时,能同步触发自定义组件的表单校验? 解决方案 将表单绑定的变量传入自定义组件中,在自定义组件中定义表单校验规则 完整代码范例 表单 内嵌自定义…...