vue-vue2和vue3响应式原理的区别

news/2024/4/25 1:40:30/文章来源:https://blog.csdn.net/weixin_42855188/article/details/130206072

核心要点

  • vue2数据响应式的实现
  • vue3数据响应式的实现
  • vue2和vue3响应式原理的区别

1、vue2数据响应式

vue 2 是通过 Object.defineProperty 来实现数据 读取和更新时的操作劫持,通过更改默认的 getter/setter 函数,在 get 过程中收集依赖,在 set 过程中派发更新的;

通过下面的简易代码来分析

// 响应式数据处理,构造一个响应式对象
class Observer {constructor(data) {this.data = datathis.walk(data)}// 遍历对象的每个 已定义 属性,分别执行 defineReactivewalk(data) {if (!data || typeof data !== 'object') {return}Object.keys(data).forEach(key => {this.defineReactive(data, key, data[key])})}// 为对象的每个属性重新设置 getter/setterdefineReactive(obj, key, val) {// 每个属性都有单独的 dep 依赖管理const dep = new Dep()// 通过 defineProperty 进行操作代理定义Object.defineProperty(obj, key, {enumerable: true,configurable: true,// 值的读取操作,进行依赖收集get() {if (Dep.target) {dep.depend()}return val},// 值的更新操作,触发依赖更新set(newVal) {if (newVal === val) {return}val = newValdep.notify()}})}
}// 观察者的构造函数,接收一个表达式和回调函数
class Watcher {constructor(vm, expOrFn, cb) {this.vm = vmthis.getter = parsePath(expOrFn)this.cb = cbthis.value = this.get()}// watcher 实例触发值读取时,将依赖收集的目标对象设置成自身,// 通过 call 绑定当前 Vue 实例进行一次函数执行,在运行过程中收集函数中用到的数据// 此时会在所有用到数据的 dep 依赖管理中插入该观察者实例get() {Dep.target = thisconst value = this.getter.call(this.vm, this.vm)// 函数执行完毕后将依赖收集目标清空,避免重复收集Dep.target = nullreturn value}// dep 依赖更新时会调用,执行回调函数update() {const oldValue = this.valuethis.value = this.get()this.cb.call(this.vm, this.value, oldValue)}
}// 依赖收集管理者的构造函数
class Dep {constructor() {// 保存所有 watcher 观察者依赖数组this.subs = []}// 插入一个观察者到依赖数组中addSub(sub) {this.subs.push(sub)}// 收集依赖,只有此时的依赖目标(watcher 实例)存在时才收集依赖depend() {if (Dep.target) {this.addSub(Dep.target)}}// 发送更新,遍历依赖数组分别执行每个观察者定义好的 update 方法notify() {this.subs.forEach(sub => {sub.update()})}
}Dep.target = null// 表达式解析
function parsePath(path) {const segments = path.split('.')return function (obj) {for (let i = 0; i < segments.length; i++) {if (!obj) {return}obj = obj[segments[i]]}return obj}
}

这里省略了数组部分,但是 数组本身的响应式监听 是通过重写数组方法来实现的,而 每个数组元素 则会再次进行 Observer 处理(需要数组在定义时就已经声明的数组元素)。

因为 Object.definePorperty 只能对 对象的已知属性 进行操作,所有才会导致 没有在 data 中进行声明的对象属性直接赋值时无法触发视图更新,需要通过($set)来处理。

而数组因为是通过重写数组的7个方法【 ‘push’,‘pop’,‘shift’,‘unshift’, ‘splice’,‘sort’,‘reverse’】和遍历数组元素进行的响应式处理,也会导致按照数组下标进行赋值或者更改元素时无法触发视图更新

<body><div id="app" class="demo-vm-1"><p>{{arr[0]}}</p><p>{{arr[2]}}</p><p>{{arr[3].c}}</p></div>
</body><script>new Vue({el: "#app",data() {return {arr: [1, 2, { a: 3 },{ c: 5 }]}},mounted() {console.log("demo Instance: ", this.$data);setTimeout(() => {console.log('update')this.arr[0] = { o: 1 } //设置完后,发现页面展示的数据不会更新this.arr[2] = { a: 1 } //设置完后,发现页面展示的数据不会更新},2000)},})
</script>

因为数组元素的前三个元素 在定义时都是简单类型,所以即使在模板中使用了该数据,也无法进行依赖收集和更新响应

2、vue 3 的响应式实现

vue 3 采用了全新的 Proxy 对象来实现整个响应式系统基础,Proxy 是 ES6 新增的一个构造函数,用来创建一个 目标对象的代理对象,拦截对原对象的所有操作;用户可以通过注册相应的拦截方法来实现对象操作时的自定义行为;

但是 只有通过 proxyObj 进行操作的时候才能通过定义的操作拦截方法进行处理,直接使用原对象则无法触发拦截器,这也是 Vue 3 中要求的 reactive 声明的对象修改原对象无法触发视图更新的原因;

并且 Proxy 也只针对 引用类型数据 才能进行代理,所以这也是 Vue 的基础数据都需要通过 ref 进行声明的原因,内部会建立一个新对象保存原有的基础数据值;

// vue3响应式原理let toProxy = new WeakMap() // 原对象:代理过的对象
let toRaw = new WeakSet() // 代理过的对象:原对象function isObject(val) {return typeof val === 'object' && val !== 'null'
}function reactive(target) {// 创建响应式对象return createReactiveObject(target)
}function createReactiveObject(target) { // 创建代理后的响应式对象if (!isObject(target)) { // 如果不是对象,直接返回return target}let proxy = toProxy.get(target) // 如果对象已经被代理过了,直接返回if(proxy) {return proxy}let baseHandler = {//receiver:被代理后的对象get(target,key,receiver) { console.log('获取');// receiver.get() ==》 new proxy().get 这会报错,也就意味着我们不能直接取到被代理对象上的属性,这时候我们需要用到Reflect,这其实也是一个对象,它只不过也含有一些明显属于对象上的方法,且和proxy上的方法一一对应let result = Reflect.get(target,key,receiver)//递归多层代理,相比于vue2的优势是,vue2默认递归,而vue3中,只要不使用就不会递归。return isObject(result) ? reactive(result) : result },set(target,key,value,receiver) {let hadkey = target.hasOwnProperty(key)let oldValue = target[key]if(!hadkey) {console.log('新增');} else if (oldValue !== value) {console.log('修改');}let res = Reflect.set(target,key,value,receiver)return res},deleteProperty(target,key) {console.log('删除');let res = Reflect.deleteProperty(target,key)return res}}let observed = new Proxy(target, baseHandler)toProxy.set(target, observed)toRaw.add(observed,target)return observed
}

3、vue2和vue3响应式原理的区别

  • vue2使用 Object.defineProperty() 实现,而vue3使用 Proxy() 实现
  • vue2 Object.defineProperty 不兼容 IE8,vue3 Proxy 不兼容 IE11
  • vue2 Object.defineProperty 是劫持对象属性,vue3 Proxy是代理整个对象
  • vue2 Object.defineProperty 不能监听到数组下标变化和对象新增属性,vue3 Proxy 可以
  • vue2 Object.defineProperty 会污染原对象,修改时是修改原对象,vue3 Proxy是对原对象进行代理并会返回一个新的代理对象,修改的是代理对象
  • vue2 Object.defineProperty 局限性大,只能针对单属性监听,所以在一开始就要全部递归监听,Proxy 对象嵌套属性运行时递归,用到才代理,性能提升很大,首次渲染更快
  • vue2 数组响应式的实现是通过 重写数组的原型方法实现,而vue3通过Proxy实现
  • vue3 拦截操作更加多样,多达13种拦截方法

4、参考博客

  • Vue2、Vue3响应式原理的区别
  • Vue2与Vue3响应式原理与依赖收集详解
  • Vue3 和 Vue2 的响应式原理区别

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

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

相关文章

《编码——隐藏在计算机软硬件背后的语言》精炼——第11章(门)

“The only source of knowledge is experience.” - Albert Einstein 引言 编码是一种处理并表达信息的方式&#xff0c;它包括摩斯电码、盲文、二进制语言等等&#xff0c;当然作为计算机类的经典书籍&#xff0c;这本书简述了计算机中以二进制数为基础的编码方式&#xff0…

nginx简单介绍

文章目录 1. 下载并解压2. 80端口被占用&#xff0c;更改nginx默认的监听端口3. 访问nginx4. 在linux上安装nginx5. nginx常用命令6. nginx.conf 1. 下载并解压 官网下载 2. 80端口被占用&#xff0c;更改nginx默认的监听端口 更改conf/nginx.conf文件 3. 访问nginx ht…

【Linux】popen pclose接口介绍

本篇文章简单讲述了c语言接口popen/pclose的用法 1.函数作用 函数定义如下 #include <stdio.h>FILE *popen(const char *command, const char *type); int pclose(FILE *stream);1.1 popen popen函数会创建一个管道&#xff0c;fork后调用shell来打开进程。由于管道的…

射频封装技术:层压基板和无源器件集成

射频和无线产品领域可以使用非常广泛的封装载体技术&#xff0c;它们包括引线框架、层压基板、低温共烧陶瓷&#xff08;LTCC&#xff09;和硅底板载体&#xff08;Si Backplane&#xff09;。由于不断增加的功能对集成度有了更高要求&#xff0c;市场对系统级封装方法&#xf…

Qt 项目Mingw编译器转换为VS编译器时的错误及解决办法

错误 在mingw生成的项目&#xff0c;转换为VS编译器时通常会报些以下错误&#xff08;C4819警告&#xff0c;C2001错误&#xff0c;C2143错误&#xff09; 原因及解决方式 这一般是由于字符编码引起的&#xff0c;在源代码文件中包含了中文字符导致的。Qt Creator 生成的代码文…

iptables防火墙和Firewalld

引言 在 Internet 中&#xff0c;企业通过各种应用系统来为用户提供各种服务&#xff0c;如 Web 网站、电子邮件系统、FTP 服务器、数据库系统等&#xff0c;那么&#xff0c;如何来保护这些服务器&#xff0c;过滤企业不需要的访问甚至是恶意的入侵呢&#xff0c;接下来&#…

【Linux】生产者消费者模型——环形队列RingQueue(信号量)

文章目录 铺垫信号量信号量概念信号量PV操作信号量基本接口 环形队列的生产消费模型引入环形队列访问环形队列代码实现代码改造多生产者多消费者代码 总结 铺垫 之前写的代码是存在不足的地方的&#xff1a; 我们使用线程操作临界资源的时候要先去判断临界资源是否满足条件&am…

最新动态 | 大势智慧参加广东省应急测绘保障与安全生产演练

4月20日&#xff0c;2023年度广东省应急测绘保障与安全生产演练在台山市赤溪镇鱼塘湾举行。本次演练由广东自然资源厅主办&#xff0c;广东省国土资源测绘院、江门市自然资源局和台山市人民政府承办。在省市各指导单位与参演单位的多方协同与指挥下&#xff0c;应急测绘保障与安…

【三十天精通Vue 3】第十四天 Vue 3 的单元测试详解

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: 三十天精通 Vue 3 文章目录 引言一、为什么要进行单元测试1.1 单元测试的概念1.2 单元测试的优…

ctfshow_WEB_web2 wp

前言 写这个是因为。。。我想摆烂&#xff0c;就去从最简单的题开始做了&#xff0c;想着交一道题是一道嘛&#xff0c;总之觉得这样做很适合欺骗安慰自己&#xff08;逃 然后我发现我错了&#xff0c;我第二道题就做了好久还没做出来&#xff0c;甚至最后去点开了hint…… ps…

Java网络编程系列之NIO

Java网络编程系列之NIO 1.Java NIO概述1.1 阻塞IO1.2 非阻塞IO1.3 NIO概述1.3.1 Channels1.3.2 Buffer1.3.3 Selector 2.Java NIO(Channel)2.1Channel概述2.2 Channel实现2.3 FileChannel 介绍与示例2.4 FileChannel 操作详解2.4.1 打开FileChannel2.4.2 从FileChannel读取数据…

自定义测试平台搭建

体验地址&#xff1a;TestManagePlatform 首次加载会比较慢... 功能点 1.数据工具生成&#xff0c;增删改查 2.测试用例以及测试套件生成&#xff0c;测试执行测试基础用例增删改查。 3.Jacoco 代码增量扫描 4.文章管理 欢迎私聊&#xff0c;支撑自定义开发。

Java基础(十)字符串相关类

1 字符串相关类之不可变字符序列&#xff1a;String 1.1 String的特性 java.lang.String 类代表字符串。Java程序中所有的字符串文字&#xff08;例如"hello" &#xff09;都可以看作是实现此类的实例。 字符串是常量&#xff0c;用双引号引起来表示。它们的值在创…

对数据结构的初步认识

前言: 牛牛开始更新数据结构的知识了.本专栏后续会分享用c语言实现顺序表,链表,二叉树,栈和队列,排序算法等相关知识,欢迎友友们互相学习,可以私信互相讨论哦! &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&a…

Allegro PCB后处理

Allegro PCB后处理&#xff0c;主要是完成线路设计以后&#xff0c;输出生产文件之前的处理。这是看教程做的记录&#xff0c;方便以后自己参考。 教程&#xff1a; [小哥Cadence Allegro 132讲字幕版PCB视频教程]_哔哩哔哩_bilibili 感觉关键是多看右边Options菜单&#xff0…

Simulation Extractable Versions of Groth’s zk-SNARK Revisited学习笔记

1. 引言 等人2020年论文《Simulation Extractable Versions of Groth’s zk-SNARK Revisited》&#xff0c;开源代码实现见&#xff1a; https://github.com/Baghery/ABPR22&#xff08;Rust&#xff0c;基于arkworks开发。使用了Multi-Scalar Multiplication (MSM)技术来优化…

linux下使用ftp下载服务器数据

首先确认服务器地址 比如我要从uniprot获取数据&#xff0c;需要先ping服务器地址&#xff1a; 可见&#xff0c;ftp地址不需要前面的https 匿名登录 匿名&#xff1a;anonymous 密码&#xff1a;任意邮箱&#xff0c;如123163.com 这里的传输模式我使用的是二进制&#xff…

Revit进入Unity教程

一、背景 小伙伴们是否有Revit进入Unity交互的需求呢&#xff1f; 二、实现功能 1.Revit进入Unity,包含模型属性&#xff0c;材质&#xff0c;轻量化等 2.实现BIM构件点选&#xff0c;高亮&#xff0c;属性展示 3.实现BIM模型分层显示&#xff0c;爆炸等效果 学习教程&…

xilinx zynq+vitis实现命令行编译输出xsa以及bin文件

执行菜单命令【开始】—【所有程序】—【Xilinx Design Tools】—【Vivado2020.1】—【Vivado2020.1Tcl Shell】,弹出命令界面 或者cmd命令下输入call D:\soft_install\vivado2020.1\Vivado\2020.1\bin\vivado.bat -mode tcl 2.输入打开工程指令&#xff1a; open_project …

SpringBoot整合Redis,一篇带你入门使用Redis

本文介绍如何将Redis整合到SpringBoot项目中&#xff0c;以及如何配置、封装和使用 文章目录 前言环境搭建项目结构添加依赖 Module封装RedisConfig配置封装常见操作为ServiceRedisServiceRedisLockUtil 测试 前言 参考链接&#xff1a; 英文官网链接中文官网链接Redis githu…