vue2-vue3响应式原理

  • 我们先来看一下响应式意味着什么?我们来看一段代码:
  1. m有一个初始化的值,有一段代码使用了这个值;
  2. 那么在m有一个新的值时,这段代码可以自动重新执行;
let m = 20
console.log(m)
console.log(m * 2)m = 40
  • 上面的这样一种可以自动响应数据变量的代码机制,我们就称之为是响应式的。
  1. 那么我们再来看一下对象的响应式:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

响应式函数设计

  • 首先,执行的代码中可能不止一行代码,所以我们可以将这些代码放到一个函数中:
  1. 那么我们的问题就变成了,当数据发生变化时,自动去执行某一个函数;
  • 但是有一个问题:在开发中我们是有很多的函数的,我们如何区分一个函数需要响应式,还是不需要响应式呢?
  1. 很明显,下面的函数中 foo 需要在obj的name发生变化时,重新执行,做出相应;
  2. bar函数是一个完全独立于obj的函数,它不需要执行任何响应式的操作;
function foo(){let newName = obj.nameconsole.log(obj.name)
}
function bar(){const result = 20 + 30console.log(result)console.log("Hello world")
}

响应式函数的实现watchFn

  • 但是我们怎么区分呢?
  1. 这个时候我们封装一个新的函数watchFn;
  2. 凡是传入到watchFn的函数,就是需要响应式的;
  3. 其他默认定义的函数都是不需要响应式的;
const reactiveFns = []function watchFn(fn){reactiveFns.push(fn)fn()
}
watchFn(function(){let newName = obj.nameconsole.log(obj.name)
})watchFn(function(){console.log("my name is "+obj.name)
})

响应式依赖的收集

  • 目前我们收集的依赖是放到一个数组中来保存的,但是这里会存在数据管理的问题:
  1. 我们在实际开发中需要监听很多对象的响应式;
  2. 这些对象需要监听的不只是一个属性,它们很多属性的变化,都会有对应的响应式函数;
  3. 我们不可能在全局维护一大堆的数组来保存这些响应函数;
  • 所以我们要设计一个类,这个类用于管理某一个对象的某一个属性的所有响应式函数:
  1. 相当于替代了原来的简单 reactiveFns 的数组;
class Depend{constructor(){this.reactiveFns = []}addDepend(fn){this.reactiveFns.push(fn)}notify(){this.reactiveFns.forEach(fn => {fn()})}
}
const dep = new Depend()function watchFn(fn){dep.addDepend(fn)fn()
}

监听对象的变化

  • 那么我们接下来就可以通过之前学习的方式来监听对象的变量:
  1. 方式一:通过 Object.defineProperty的方式(vue2采用的方式);
  2. 方式二:通过new Proxy的方式(vue3采用的方式);
  • 我们这里先以Proxy的方式来监听:
const proxyObj = new Proxy(obj,{get:function(target,key,receiver){Reflect.get(target,key,reaceiver)},set:function(target,key,value,receiver){console.log("设置了新的值",key,value)Reflect.set(target,key,value,receiver)dep.notify()}
})

对象的依赖管理

  • 我们目前是创建了一个Depend对象,用来管理对于name变化需要监听的响应函数:
  1. 但是实际开发中我们会有不同的对象,另外会有不同的属性需要管理;
  2. 我们如何可以使用一种数据结构来管理不同对象的不同依赖关系呢?
  • 在前面我们刚刚学习过WeakMap,并且在学习WeakMap的时候我讲到了后面通过WeakMap如何管理这种响应式的数据依赖:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对象依赖管理的实现

  • 我们可以写一个getDepend函数专门来管理这种依赖关系:
const targetMap = new WeakMap();
function getDepends(obj,key){// 根据对象获取对应的Map对象if(!objMap){objMap = new Map()targetMap.set(obj,objMap)}// 根据key获取Depend对象let depend = objMap.get(key)if(!depend){depend = new Depend()objMap.set(key,depend)}
}
const proxyObj = new Proxy(obj,key,receiver){get:function(target,key,receiver){return Reflect.get(target,key,receiver)},set:function(target,key,value,receiver){Reflect.set(target,key,value,receiver)const dep = getDepends(target,key)dep.notify()}

正确的依赖收集

  • 我们之前收集依赖的地方是在 watchFn 中:
  1. 但是这种收集依赖的方式我们根本不知道是哪一个key的哪一个depend需要收集依赖;
  2. 你只能针对一个单独的depend对象来添加你的依赖对象;
  • 那么正确的应该是在哪里收集呢?应该在我们调用了Proxy的get捕获器时
  1. 因为如果一个函数中使用了某个对象的key,那么它应该被收集依赖
let reactiveFn = null
function watchFn(fn){// dep.addDepend(fn)reactiveFn = fnfn()reactiveFn = null
}
const proxyObj = new Proxy(obj,{
get:function(target,key,receiver){const dep = getDepends(target,key)dep.addDepend(reactiveFn)return Reflect.get(target,key,reaceiver)},set:function(target,key,value,receiver){Reflect.set(target,key,value,receiver)const dep = getDepends(target,key)dep.notify()}
})

对Depend重构

  • 但是这里有两个问题:
  1. 问题一:如果函数中有用到两次key,比如name,那么这个函数会被收集两次;
  2. 问题二:我们并不希望将添加reactiveFn放到get中,以为它是属于Dep的行为;
  • 所以我们需要对Depend类进行重构:
  1. 解决问题一的方法:不使用数组,而是使用Set;
  2. 解决问题二的方法:添加一个新的方法,用于收集依赖;
class Depend {constructor(){this.reactiveFns = new Set()}addDepend(fn){}depend(){if(reactiveFn){this.reactiveFns.add(reactiveFn)}}notify(){}
}
const proxyObj = new Proxy(obj,{get:function(target,key,receiver){dep.depend()return Reflect.get(target,key,receiver)},set:function(target,key,value,receiver){Reflect.set(target,key,value,receiver)const dep = getDepends(target,key)dep.notify()}
})

创建响应式对象

  • 我们目前的响应式是针对于obj一个对象的,我们可以创建出来一个函数,针对所有的对象都可以变成响应式对象:
function reactive(obj){return new Proxy(obj,{get:function(target,key,receiver){const dep = getDepends(target,key)dep.depend()return Reflect.get(target,key,receiver)},set:function(target,key,value,receiver){Reflect.set(target,key,value,receiver)const dep = getDepends(target,key)dep.notify()}})
}
const obj2 = reactive({address:"广州市"
})watchFn(function(){console.log("我的地址:",obj2.address)
})obj2.address = "北京市"

Vue2响应式原理

  • 我们前面所实现的响应式的代码,其实就是Vue3中的响应
    式原理:
  1. Vue3主要是通过Proxy来监听数据的变化以及收集相关
    的依赖的;
  2. Vue2中通过我们前面学习过的Object.defineProerty
    的方式来实现对象属性的监听;
  • 我们可以将reactive函数进行如下的重构:
  1. 在传入对象时,我们可以遍历所有的key,并且通过属
    性存储描述符来监听属性的获取和修改;
  2. 在setter和getter方法中的逻辑和前面的Proxy是一致
    的;
function reactive2(obj){Object.keys(obj).forEach(key => {let value = obj[key]Object.defineProperty(obj,key,{get:function(){const dep = getDepends(obj,key)dep.depend()return value},set:function(newValue){const dep = getDepends(obj,key)value = newValuedep.notify()}})})return obj
}

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

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

相关文章

计算机网络-IP组播基础

一、概述 在前面的学习交换机和路由协议,二层通信是数据链路层间通信,在同一个广播域间通过源MAC地址和目的MAC地址进行通信,当两台主机第一次通信由于不清楚目的MAC地址需要进行广播泛洪,目的主机回复自身MAC地址,然后…

MySQL数据库树状结构查询

一、树状结构 MySQL数据库本身并不直接支持树状结构的存储,但它提供了足够的灵活性,允许我们通过不同的方法来模拟和实现树状数据结构。具体方法看下文。 数据库表结构: 实现效果 查询的结果像树一样 二、使用 以Catalog数据表&#xff0c…

昇思25天学习打卡营第18天 | 基于MobileNetv2的垃圾分类

内容介绍: MobileNet网络是由Google团队于2017年提出的专注于移动端、嵌入式或IoT设备的轻量级CNN网络,相比于传统的卷积神经网络,MobileNet网络使用深度可分离卷积(Depthwise Separable Convolution)的思想在准确率小…

CSS选择器:nth-child()

CSS3 :nth-child() 选择器 :nth-child(n) 选择器匹配属于其父元素的第 N 个子元素,不论元素的类型。 n 可以是数字、关键词或公式。 Odd 和 even 是可用于匹配下标是奇数或偶数的子元素的关键词(第一个子元素的下标是 1)。 使用公式 (an b)。…

Nginx-http_limit_req_module模块

文章目录 前言一、ngx_http_limit_req_module模块二、指令1.limit_req_zone2.limit_req3.limit_req_log_level4.limit_req_status 实验burst取默认0的情况burst不取默认值 总结 前言 如何限制每个客户端每秒处理请求数 一、ngx_http_limit_req_module模块 生效阶段&#xff1…

间接平差——以水准网平差为例 (matlab详细过程版)

目录 一、原理概述二、案例分析三、代码实现四、结果展示本文由CSDN点云侠原创,间接平差——以水准网平差为例 (matlab详细过程版),爬虫自重。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT生成的文章。 一、原理概述 间接平差的函数模型和随机模型…

加盖骑缝章软件、可以给PDF软件加盖自己的骑缝章

加盖骑缝章的软件多种多样,尤其是针对PDF文件,有多种软件可以实现给PDF文件加盖自己的骑缝章。以下是一些常用的软件及其特点: 1. Adobe Acrobat Pro DC 特点: 多功能PDF编辑:Adobe Acrobat Pro DC是一款功能强大的…

加入运动健康数据开放平台,共赢鸿蒙未来

HarmonyOS SDK运动健康服务(Health Service Kit)是为华为生态应用打造的基于华为帐号和用户授权的运动健康数据开放平台。在获取用户授权后,开发者可以使用运动健康服务提供的开放能力获取运动健康数据,基于多种类型数据构建运动健…

【Qwen2部署实战】Ollama上的Qwen2-7B:一键部署大型语言模型指南

系列篇章💥 No.文章1【Qwen部署实战】探索Qwen-7B-Chat:阿里云大型语言模型的对话实践2【Qwen2部署实战】Qwen2初体验:用Transformers打造智能聊天机器人3【Qwen2部署实战】探索Qwen2-7B:通过FastApi框架实现API的部署与调用4【Q…

Python创建MySQL数据库

一、使用Docker部署本地MySQL数据库 docker run --restartalways -p 3307:3306 --name mysql -e MYSOL_ROOT_PASSWORDlms123456 -d mysql:8.0.25 参数解析: 用户名:root 密码:lms123456 端口:3307 二、在Pycharm开发工具中配置连接MySQL数据库 三、安装zdppy_mysql pip inst…

沉浸式三维园区场景漫游体验

利用图扑三维可视化技术展示园区在不同时间段的变化,提供全景漫游体验,帮助用户全方位感受和理解园区环境,实现智能化管理与优化。

Linux 进程间的信号

1.信号的初认识 信号是进程之间事件异步通知的一种方式,属于软中断。通俗来说信号就是让用户或进程给其他用户和进程发生异步信息的一种方式。对于信号我们可以根据实际生活,对他有以下几点认识:1.在没有产生信号时我们就知道产生信号要怎么处…

LLM - 卷积神经网络(CNN)

1. 卷积神经网络结构:分为输入层,卷积层,池化层,全连接层; (1)首先进入输入层,对数据数据进行处理,将输入数据向量化处理,最终形成输入矩阵。 (…

电脑录歌用什么软件好?分享电脑录音软件:6款

短视频普遍的今天,越来越多的人喜欢通过电脑进行音乐创作和录制。然而,面对市面上琳琅满目的电脑录音软件,很多人可能会感到困惑:电脑录歌用什么软件好呢?本文将为大家分享六款精选的录音软件,帮助大家找到…

校园电动车安全监控和调度系统-计算机毕业设计源码13028

摘要 校园电动车安全监控和调度系统是为了确保校园内电动车的安全和高效运行而设计的。该系统通过安装在电动车上的监控设备,实时监测电动车的运行状态,包括速度、位置、电池电量等,一旦发现异常情况,系统会立即发出警报并通知相关…

【matlab】分类回归——智能优化算法优化径向基神经网络

径向基(Radial Basis Function, RBF)神经网络 一、基本概念 径向基函数(RBF):是一个取值仅仅依赖于离原点(或某一中心点)距离的实值函数。在RBF神经网络中,最常用的径向基函数是高…

使用patch-package自动修改node_modules中的内容/打补丁

背景 在使用VuePress搭建个人博客的过程中,我需要使用到一个用来复制代码块的插件uepress-plugin-nuggets-style-copy。 问题:插件可以正常安装,但是启动会报错。通过查看错误信息,定位是插件中的copy.vue文件出现错误&#xff0c…

将循环转化为递归的三种方法,求1+2+3……+n等差数列

解法一&#xff1a;使用公共变量s&#xff0c;递归循环1~n加到s上 #include<bits/stdc.h> using namespace std; int n,s; void fun(int i){if(i<n){ssi;fun(i1);}}int main(){cin>>n;fun(1);cout<<s;return 0; } 解法二&#xff1a;通过层层累加&#x…

C语言自定义类型——联合体、枚举

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、联合体&#xff08;一&#xff09;、联合体的声明&#xff08;二&#xff09;、联合体的特点&#xff08;三&#xff09;、联合体大小的计算&#xff01;&a…

二维树状数组区域查询

落谷4514 过关代码如下 #define _CRT_SECURE_NO_WARNINGS #include<bits/stdc.h> using namespace std; //#define int long longconst int N 2050; int t1[N][N], t2[N][N], t3[N][N], t4[N][N]; int lowbit(int x) { return x & (-x); } int n, m; void update(…