前端开发攻略---从源码角度分析Vue3的Propy比Vue2的defineproperty到底好在哪里。一篇文章让你彻底弄懂响应式原理。

本文主要是介绍前端开发攻略---从源码角度分析Vue3的Propy比Vue2的defineproperty到底好在哪里。一篇文章让你彻底弄懂响应式原理。,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、思考

Vue的响应式到底要干什么?

  • 无非就是要知道当你读取对象的时候,要知道它读了。要做一些别的事情
  • 无非就是要知道当你修改对象的时候,要知道它改了。要做一些别的事情
  • 所以要想一个办法,把读取和修改的动作变成一个函数,读取和修改的时候分别调用对应的函数
  • 在ES6之,只能通过Object.defineproperty 给它变成一个get和set函数。当读取这个属性的时候运行get,修改这个属性的时候运行set

  • 在ES6之后,就能通过Porxy去代理整个对象

2、Vue2的做法 

针对某个对象中某个属性的做法

通过Object.defineProperty去针对某个对象的属性去进行监听                           

const obj = {a: 1,b: 2,c: {d: 3,e: 4,},
}// 保存初始值
let v = obj.aObject.defineProperty(obj, 'a', {get() {console.log('a', '读取')return v},set(val) {// 当原来的值与重新赋值的值不一样的时候才进行修改if (val !== v) {console.log('a', '更改了')v = val}},
})

当我们去读取 obj.a 这个属性的时候 get 函数 就会调用。

当我们去修改 obj.a 这个属性的时候 set 函数 就会调用。

针对某个对象中多个属性的做法

Vue2的源码中,有一个函数叫做 observe(观察器)。在这个函数中,去深度遍历对象中的每一个属性,给每一个属性添加 Object.defineProperty,这样就能对对象中的每一个属性叫做监听。这个过程就叫做 观察

const obj = {a: 1,b: 2,c: {d: 3,e: 4,},
}
// 辅助函数 判断这个值是不是一个对象
function _isObject(v) {return typeof v === 'object' && v !== null
}
function observe(obj) {for (const k in obj) {let v = obj[k]// 如果这一个属性仍然是一个对象的话,就需要深度遍历if (_isObject(v)) {observe(v)}Object.defineProperty(obj, k, {get() {console.log(k, '读取了')return v},set(val) {// 当原来的值与重新赋值的值不一样的时候才进行修改if (val !== v) {console.log(k, '更改了')v = val}},})}
}
observe(obj)

当我们去读取对象中的某个属性的时候 get 函数 就会调用。

当我们去修改对象中的某个属性的时候 set 函数 就会调用。

打印内容解释:

  • obj.a  =  3   更改
  • obj.c.d  = 4  先读取 obj.c 的值,再更改 obj.c.d 的值
  • obj.c.e   先读取 obj.c 的值,再读取 obj.c.e 的值

总结

  1. 在Vue2里面观察的方式就是 深度遍历每一个属性 把每一个属性的读取和赋值变成函数get和set。
  2. 在这种做法下有一个天生的缺陷,由于它是针对每个属性的监听,所以就必须要进行深度的遍历,这样会有效率损失。 
  3. 由于在 observe(obj) 观察这个步骤里边完成了深度遍历,也就是说在这个时间点里边,这些属性被我们监听到了都被改成get和set了
  4. 但是这一步一旦做完了之后。再去新增的话它就不知道了。比如 obj.qwertr = 3 对于这个属性而言,它就是没有监听的,因为监听的步骤已经结束了

  5. 这就是为什么Vue2它无法监听属性的新增,当然也包括属性的删除。它也收不到通知。因为在 Object.defineProperty 既不会运行get也不会运行set

3、Vue3的做法

其实核心道理都是一样的。无论是Vue2还是Vue3,都必须要把读取和赋值变成函数。只不过Vue3变成函数的方式不一样。在Vue3里面不会去对对象的每一个属性进行监听了,而是直接监听整个对象。将来不管是在这个对象中添加还是删除属性都不怕了。因为监听的是整个对象,这要动了这个对象就能收到通知。

做法

Vue3使用的是Proxy

const obj = {a: 1,b: 2,c: {d: 3,e: 4,},
}const proxy = new Proxy(obj, {// 读这个对象的属性的时候收到通知get(target, k) {// target就是obj,k是属性名let v = target[k]console.log(k, '读取')return v},// 修改这个对象的属性的时候收到通知set(target, k, val) {// target就是obj,k是属性名,val是新值if (target[k] !== val) {target[k] = valconsole.log(k, '更改')return target[k]}},// 删除对象的属性的时候收到通知deleteProperty(target, k, val) {console.log(k, '删除')return target[k]},
})

会产生一个代理对象propx,将来去读属性也好,重新给属性赋值也好都是通过这个代理对象去做的。

总结

  1. Proxy 对象可以直接代理整个对象,而不需要遍历对象属性进行劫持,这样可以减少运行时的性能开销。在 Vue 2 中,由于每个属性都需要单独设置 get 和 set,对于大量的属性或嵌套属性,这种劫持可能会导致性能下降。
  2. 另外,使用 Proxy 的方式更符合现代 JavaScript 的发展趋势,更好地利用了 JavaScript 引擎的优化。

4、总结

综上所述,Vue 3 中使用 Proxy 对象代替了 Vue 2 中的 Object.defineProperty,带来了更强大、更灵活和更高效的属性拦截和代理功能,同时也提升了开发体验和调试效率。这些改进使得 Vue 3 在处理 Props 的方式更加现代化和优雅,提升了整体的性能和可维护性。

这篇关于前端开发攻略---从源码角度分析Vue3的Propy比Vue2的defineproperty到底好在哪里。一篇文章让你彻底弄懂响应式原理。的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/912043

相关文章

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原

Spring Boot 结合 WxJava 实现文章上传微信公众号草稿箱与群发

《SpringBoot结合WxJava实现文章上传微信公众号草稿箱与群发》本文将详细介绍如何使用SpringBoot框架结合WxJava开发工具包,实现文章上传到微信公众号草稿箱以及群发功能,... 目录一、项目环境准备1.1 开发环境1.2 微信公众号准备二、Spring Boot 项目搭建2.1 创建

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实

python中Hash使用场景分析

《python中Hash使用场景分析》Python的hash()函数用于获取对象哈希值,常用于字典和集合,不可变类型可哈希,可变类型不可,常见算法包括除法、乘法、平方取中和随机数哈希,各有优缺点,需根... 目录python中的 Hash除法哈希算法乘法哈希算法平方取中法随机数哈希算法小结在Python中,