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

从Vue2到Vue3【七】——Vue2中响应式原理的实现及其缺陷

系列文章目录

内容链接
从Vue2到Vue3【零】Vue3简介
从Vue2到Vue3【一】Composition API(第一章)
从Vue2到Vue3【二】Composition API(第二章)
从Vue2到Vue3【三】Composition API(第三章)
从Vue2到Vue3【四】Composition API(第四章)
从Vue2到Vue3【五】从Vue2到Vue3【五】——新的组件(Fragment、Teleport、Suspense)
从Vue2到Vue3【六】从Vue2到Vue3【六】——Vue3的改变(文末送书)

文章目录

  • 系列文章目录
  • 前言
  • 一、Vue2中的响应式原理
  • 二、模拟简易版响应式原理
  • 三、Vue2响应式数据带来的缺陷
    • 3.1 新增属性的响应问题
    • 3.2 数组变动的响应问题
    • 3.3 对象属性的删除问题
  • 四、Vue2响应式缺陷的解决办法
    • 4.1 新增属性的响应问题
    • 4.2 数组变动的响应问题
    • 4.3 对象属性的删除问题
  • 总结


前言

Vue是一款流行的JavaScript框架,它以其简洁易用的语法和强大的响应式能力而受到广泛关注。Vue的响应式数据机制是其核心特性之一,它能够自动追踪数据的变化,并实时更新相关的视图。然而,Vue 2中的响应式数据机制并非完美无缺,本文将探讨Vue 2响应式原理及其存在的缺陷。


一、Vue2中的响应式原理

  • Vue 2的响应式原理:
    在Vue 2中,响应式是通过使用Object.defineProperty()方法来实现的
    在组件实例化过程中,Vue会对数据对象(data)进行递归地遍历,将每个属性都转换为getter/setter,并且为每个属性创建一个依赖追踪的系统。当属性被访问或修改时,getter/setter会触发依赖追踪系统,从而进行依赖收集派发更新,以保证数据和视图的同步。
  • 具体实现步骤如下:
    1.创建Observer对象:通过递归地将data对象的属性转换为响应式属性,使用Object.defineProperty()为每个属性添加getter和setter方法Vue2中 通过使用 Object.defineProperty() 方法,将对象的属性转换成 getter 和 setter,当数据发生变化时,会自动触发相应的更新函数,实现数据的响应式。
    2.创建Dep对象:用来管理 Watcher,它用来收集依赖、删除依赖和向依赖发送消息等。用于解耦属性的依赖收集和派发更新操作。
    3.创建Watcher对象:Watcher对象用于连接视图和数据之间的桥梁,当被依赖的属性发生变化时Watcher对象会接收到通知并更新视图。当数据发生变化时,它会通知订阅该数据的组件更新视图。Watcher 在实例化时会将自己添加到 Dep 中,当数据发生变化时,会触发相应的更新函数。
    4.模板编译:Vue会解析模板,将模板中的数据绑定指令转译为对应的更新函数,以便在数据发生变化时调用。

在修改对象的值的时候,会触发对应的 setter, setter通知之前依赖收集得到的 Dep 中的Watcher,告诉它自己的值改变了,需要重新渲染视图。这时候这些 Watcher就会开始调用 update 来更新视图, 对应的getter触发追踪,把新值重新渲染到视图上

input用v-model绑定数据,我们需要在input元素上添加事件监听,每当input事件被触发时,就修改对应的data,data里的数据又会响应式更新回视图

二、模拟简易版响应式原理

实现思路:
定义一个Observe构造函数用于对data对象的属性进行数据劫持。我们使用Object.defineProperty()方法对data对象的每个属性进行劫持,定义了属性的getter和setter方法。
在getter方法中,我们返回属性的值。在setter方法中,我们判断新值是否与旧值不同,如果不同,则更新属性的值,并触发依赖更新

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>Document</title></head><body><script type="text/javascript" >let data = {name:'前端百草阁',age:21,}function Observer(obj){//汇总对象中所有的键形成一个数组const keys = Object.keys(obj)//遍历keys.forEach((k)=>{Object.defineProperty(this,k,{get(){return obj[k]},set(val){console.log(`${k}被改了,我要通知Vue重新去解析模板.....`)obj[k] = val}})})}//创建一个监视的实例对象,用于监视data中属性的变化const obs = new Observer(data)		//准备一个vm实例对象let vm = {}vm._data = data = obs</script></body>
</html>

请添加图片描述
在这里插入图片描述
这个时候,原先data里的属性就会各自有一个为他们服务的getter和setter,变成了具有响应式的属性

  • 简易式版本的缺陷
    • 缺陷一:正常vue中会做一个数据代理,当访问vm.name时,访问的其实是vm._data.name,这样做了数据代理后使用起来更方便
      在这里插入图片描述
    • 缺陷二: 简易式版本没有考虑到data里面的属性值还是对象的情况,在Vue中利用递归的方法将data里所有的属性通过递归的方式都转换为了响应式属性(即使属性值是一个数组,数组里藏了对象,依然可以把对应的属性转换为响应式属性)

这里有一个小tips,利用this指向obs,访问this(obs)里的属性,getter返回的其实是obj里的属性(数据代理),为什么要这样呢?如果说你访问obj里的属性,我真的通过getter给你返回了obj里对应的属性,返回的obj里的属性又要去触发自己的getter,那是不是就陷入死循环了呢?导致的问题就是无论你是触发getter 还是setter都会导致超出最大调用堆栈这个错误

在这里插入图片描述
解决这个问题还有一个办法就是利用闭包,利用闭包把初始值传给value存起来了,后续getter和setter都是针对闭包内的value,和原本的obj隔离开了,当你访问或者设置obj.key的时候,就会去修改对应的val(由于闭包val不会被垃圾机制回收)就不存在最大调用堆栈溢出的情况了

function observe(obj) {if (!obj || typeof obj !== 'object') {return;}Object.keys(obj).forEach(function(key) {defineReactive(obj, key, obj[key]); });
}function defineReactive(obj, key, val) {observe(val);  // 递归地对data对象的属性进行数据劫持Object.defineProperty(obj, key, {get: function() {return val;},set: function(newValue) {if (newValue !== val) {val = newValue;// 触发依赖更新updateView();}}});
}function updateView() {document.querySelector('h1').innerText = vm.message;
}// 初始化数据劫持
observe(vm.$data);

在上述代码中,observe函数用于递归地对data对象的属性进行数据劫持。在defineReactive函数中,我们使用Object.defineProperty()方法对data对象的每个属性进行劫持,定义了属性的getter和setter方法。
在getter方法中,我们返回属性的值。在setter方法中,我们判断新值是否与旧值不同,如果不同,则更新属性的值,并触发依赖更新。
最后,我们调用observe(vm.$data)来初始化数据劫持,使得Vue能够捕获到对data对象属性的访问和修改操作,并触发相应的依赖更新。

三、Vue2响应式数据带来的缺陷

Vue 2中的响应式数据存在一些缺陷,但通过使用Vue提供的补救办法,可以解决大部分响应式数据的问题。

3.1 新增属性的响应问题

Vue在初始化时会对data对象的属性进行数据劫持,但是对于后续新增的属性,Vue无法自动进行响应式处理。
Vue 无法探测普通的新增属性 ,比如 this.myObject.saying = 'hi'这个新增的saying属性是不具有响应式的,Vue探测不到

3.2 数组变动的响应问题

Vue对数组的变动(例如通过索引修改数组元素、通过splice方法删除或插入元素)无法直接进行响应式处理。

例如此时在data里定义了这些数据

	data:{friends:[{name:'jerry',age:35},{name:'tony',age:36},'前端百草阁']}

在这里插入图片描述
不难发现数组中的对象都是响应式的,但数组中的普通元素却不是响应式的,意味着若直接修改数组中的元素Vue无法监测到

如果你通过数组下标修改对象属性的话是可以监测的,因为对象里的属性都是响应式的,但如果你通过数组下标修改普通元素是无法监测到的

如果用一个新数组覆盖掉原先的数组,Vue是能监测到的请添加图片描述

3.3 对象属性的删除问题

Vue无法直接检测到对象属性的删除操作。
利用delete删除对象的属性,无法被Vue监测到

四、Vue2响应式缺陷的解决办法

4.1 新增属性的响应问题

Vue.set( target, propertyName/index, value )

向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = ‘hi’)

给data中的student对象添加一个属性,并且是响应式的,有两种写法,Vue.set或者this.$set

// Vue.set(this._data.student,'sex','男') // 这里加不加_data实际上都可以,就是一个数据代理,访问谁都一样,那我们肯定选择偷懒啦
this.$set(this.student,'sex','男')  // this代表vm vue实例对象

在这里插入图片描述
实现了新增了student对象里的sex属性,并且该属性有为自己服务的getter、setter(具有响应式)

但是,Vue官网明确指出:注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
简单来说就是,set方法的第一个参数target不允许 是vm(vue实例)、也不允许是vm._data(根数据对象)

4.2 数组变动的响应问题

第一中解决办法,使用数组变异方法:Vue提供了一些数组变异方法(例如push、pop、shift、unshift、splice、sort和reverse),这些方法会触发数组的响应式更新。
如果不是这七个方法的话,比如调用slice等数组方法的话,记得要把返回的新数组覆盖掉原来的旧数组,依然能触发响应式
在这里插入图片描述
第二种解决办法,利用set方法,set方法不但能解决对象新增属性的问题,还能解决修改数组的问题(用的不多)
在这里插入图片描述

4.3 对象属性的删除问题

Vue.delete方法:用来删除对象的属性,并触发响应式更新。例如,可以使用Vue.delete(vm.someObject, ‘propertyToDelete’)来删除一个属性。
正常的delete方法,虽然确实删除了属性,但是无法被监测到
在这里插入图片描述
利用Vue.delete完美解决删除对象属性无法被监测的问题(很少用到),或者vm.$delete(vm.person,'name')
在这里插入图片描述


总结

Vue 2的响应式数据机制在大多数情况下能够满足我们的需求,但也存在一些缺陷。
首先,Vue无法直接响应新增的属性,需要使用特定的方法进行补救。其次,对于数组的变动和对象属性的删除,Vue也无法直接进行响应式处理,需要使用相应的方法来触发更新。这些缺陷在实际开发中可能会带来一些困扰。
但幸运的是,Vue提供了一些补救的办法,如Vue.set和Vue.delete方法,以及数组变异方法。通过这些补救措施,我们可以弥补Vue 2响应式数据机制的不足,提升开发效率和用户体验。尽管如此,我们也期待Vue未来版本的改进,在响应式数据方面能够更加智能和灵活,以满足更多复杂场景的需求。

相关文章:

从Vue2到Vue3【七】——Vue2中响应式原理的实现及其缺陷

系列文章目录 内容链接从Vue2到Vue3【零】Vue3简介从Vue2到Vue3【一】Composition API&#xff08;第一章&#xff09;从Vue2到Vue3【二】Composition API&#xff08;第二章&#xff09;从Vue2到Vue3【三】Composition API&#xff08;第三章&#xff09;从Vue2到Vue3【四】C…...

用C语言实现堆排序算法

1.设计思路 排序的思想将一个数组按递增的顺序进行排序&#xff0c;将数组的第一个位置空下&#xff08;下标为0&#xff09;&#xff0c;因为会导致子节点和本身同一个结点&#xff08;i和2i一致&#xff09;&#xff0c;每次堆排序在下标1的位置放上了最大值&#xff0c;然后…...

tauri在github上进行自动更新打包并发版过程,实战操作避坑

从网上找了很多很多的文章&#xff0c;结果还是入坑了&#xff0c;一个问题找了一天才解决&#xff1a; Error A public key has been found, but no private key. Make sure to set TAURI_PRIVATE_KEY environment variable. 596 ELIFECYCLE  Command failed with exit code…...

css中flex后文本溢出的问题

原因&#xff1a; 为了给flex item提供一个合理的默认最小尺寸&#xff0c;flex将flex item的min-width 和 min-height属性设置为了auto flex item的默认设置为&#xff1a; min-width&#xff1a; auto 水平flex布局 min-height&#xff1a;auto 垂直flex布局 解决办法&…...

restful接口设计规范[仅供参考]

1. 域名 应该尽量将API部署在专用域名之下。 https://api.example.com 如果确定API很简单&#xff0c;不会有进一步扩展&#xff0c;可以考虑放在主域名下。 https://www.example.org/api/2. 版本&#xff08;Versioning&#xff09; 应该将API的版本号放入URL。 http://…...

Metabase 远程代码执行(CVE-2023-38646)

漏洞描述 Metabase是一款开源数据分析及可视化工具。它可允许用户连接至各种不同类型数据源,未经身份认证的攻击者可利用本漏洞在服务器上以运行 Metabase服务器的权限进行任意命令执行。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩…...

【TiDB理论知识 07】SQL执行流程

一 DML语句读写流程 1 DML语句读流程概要 用户发出SQL 被协议层接收 Protocal Layer 通过PD获取时间戳 parse模块 解析SQL&#xff0c;通过词法解析 与 语法解析 生成AST语法树 编译SQL Compile模块 ,区分点查 与 非点查&#xff0c;生成执行计划 发送给Executor,从TIKV获…...

微服务——服务异步通讯RabbitMQ

前置文章 消息队列——RabbitMQ基本概念容器化部署和简单工作模式程序_北岭山脚鼠鼠的博客-CSDN博客 消息队列——rabbitmq的不同工作模式_北岭山脚鼠鼠的博客-CSDN博客 消息队列——spring和springboot整合rabbitmq_北岭山脚鼠鼠的博客-CSDN博客 目录 Work queues 工作队列…...

事件冒泡、事件捕获和事件委托

原文合集地址如下&#xff0c;有需要的朋友可以关注 本文地址 合集地址 什么是事件冒泡、事件捕获和事件委托&#xff1f; 事件冒泡&#xff08;Event Bubbling&#xff09;、事件捕获&#xff08;Event Capturing&#xff09;和事件委托&#xff08;Event Delegation&…...

WEB 典型安全功能说明

WEB 典型安全功能 认证Authentication 认证是指通过验证用户的身份来确认用户是否有权访问某个系统或资源。在Web安全中&#xff0c;认证是非常重要的一环&#xff0c;它可以防止未经授权的访问&#xff0c;保护用户的数据和系统的安全。 登录 登录是用户认证的常见方式之一…...

SQL编译优化原理

最近在团队的OLAP引擎上做了一些SQL编译优化的工作&#xff0c;整理到了语雀上&#xff0c;也顺便发在博客上了。SQL编译优化理论并不复杂&#xff0c;只需要掌握一些关系代数的基础就比较好理解&#xff1b;比较困难的在于reorder算法部分。 文章目录 基础概念关系代数等价 j…...

qt signal slots lambda

这里用到了qt的版本检测 连接 Combox的currentIndexChanged事件 emit来触发处理的事件 &#xff0c;进行业务或逻辑处理 这样的写法是lambda表达式的写法&#xff0c;和c#中的 (obj)>{ //todo } 类同 [](int indx){ //todo } #if QT_VERSION > QT_VERSION_CHECK(5,7,0)c…...

Spring【声明式事务】

事务简介 把一组业务当成一个业务来做&#xff1b;要么都成功&#xff0c;要么都失败&#xff01;事务在项目开发中&#xff0c;十分重要&#xff0c;涉及到数据一致性的问题&#xff0c;需要十分注意&#xff01;确保完整性和一致性&#xff01; 事务的ACID原则&#xff1a;…...

【雕爷学编程】MicroPython动手做(17)——掌控板之触摸引脚2

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…...

pytorch 中 view 和reshape的区别

在 PyTorch&#xff08;一个流行的深度学习框架&#xff09;中&#xff0c; reshape 和 view 都是用于改变张量&#xff08;tensor&#xff09;形状的方法&#xff0c;但它们在实现方式和使用上有一些区别。下面是它们之间的主要区别&#xff1a; 实现方式&#xff1a; reshap…...

认识数组指针

文章目录 数组指针的定义数组指针的应用 数组指针的定义 类比 整形数组——存放整形的数组 指针数组——存放指针的数组 整形指针——存放整形地址的指针 数组指针——存放数组地址的指针 深度理解 在之前我们知道&#xff1a;数组名表示首元素地址&#xff0c;但是有&#xf…...

SSM面试题-Spring容器的启动流程

解答: 1. BeanDefinitionReader读取配置文件(xml yml properties),创建BeanDefinition(存储bean的定义信息) 2. 配置文件读取成功后&#xff0c;将相应的配置转换成 BeanDefinition 的对象实例保存在DefaultListableBeanFactory#beanDefinitionMap 中 3. 根据配置的 BeanFacto…...

Vue 3:玩一下web前端技术(八)

前言 本章内容为VUE基础与相关技术讨论。 上一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;七&#xff09;_Lion King的博客-CSDN博客 下一篇文章地址&#xff1a; &#xff08;暂无&#xff09; 一、基础 官方文档&#xff1a;创建一个 Vue…...

AI绘画Stable Diffusion原理之Autoencoder-Latent

前言 传送门&#xff1a; stable diffusion&#xff1a;Git&#xff5c;论文 stable-diffusion-webui&#xff1a;Git Google Colab Notebook&#xff1a;Git kaggle Notebook&#xff1a;Git 今年AIGC实在是太火了&#xff0c;让人大呼许多职业即将消失&#xff0c;比如既能帮…...

C++核心知识点总结

学习一门新的程序设计语言得到最好方法就是练习编写程序&#xff01; C基础 变量和基本类型 基本内置类型 定义解释 算术类型 整型&#xff1a;包括字符和布尔类型&#xff0c;bool、char、wchar_t、char16_t、char32_t、short、int、long、long long、 浮点型&#xff1a;…...

echart折线图,调节折线点和y轴的间距(亲测可用)

options代码&#xff1a; options {tooltip: {trigger: axis, //坐标轴触发&#xff0c;主要在柱状图&#xff0c;折线图等会使用类目轴的图表中使用。},xAxis: {type: category,//类目轴&#xff0c;适用于离散的类目数据&#xff0c;为该类型时必须通过 data 设置类目数据。…...

Power BI-云端报表定时刷新--ODBC、MySQL、Oracle等其他本地数据源的刷新(二)

ODBC数据源 一些小众的数据源无法直接连接&#xff0c;需要通过微软系统自带的应用“ODBC数据源”连接。 1.首次使用应安装对应数据库的ODBC驱动程序&#xff0c;Mysql的ODBC驱动需要手动安装 2.在web服务中进行数据源的配置 Mysql数据源 1.Powerbi与Gateway第一次连SQL…...

redis 淘汰策略和持久化

文章目录 一、淘汰策略1.1 背景1.2 淘汰策略 二、持久化2.1 AOF日志2.1.1 AOF配置2.1.2 AOF策略2.1.3 AOF缺点2.1.4 AOF Rewrite2.1.5 AOF Rewrite配置2.1.6 AOF Rewrite缺点2.1.7 fork进程时的写时复制2.1.8 大key对持久化的影响 2.2 RDB快照2.2.1 RDB配置2.2.2 RDB缺点 2.3 混…...

Redis学习路线(6)—— Redis的分布式锁

一、分布式锁的模型 &#xff08;一&#xff09;悲观锁&#xff1a; 认为线程安全问题一定会发生&#xff0c;因此在操作数据之前先获取锁&#xff0c;确保线程串行执行。例如Synchronized、Lock都属于悲观锁。 优点&#xff1a; 简单粗暴缺点&#xff1a; 性能略低 &#x…...

一、创建自己的docker python容器环境;支持新增python包并更新容器;离线打包、加载image

1、创建自己的docker python容器环境 参考&#xff1a;https://blog.csdn.net/weixin_42357472/article/details/118991485 首先写Dockfile&#xff0c;注意不要有txt等后缀 Dockfile # 使用 Python 3.9 镜像作为基础 FROM python:3.9# 设置工作目录 WORKDIR /app# 复制当前…...

【Git】git企业开发命令整理,以及注意点

1.git企业开发过程 业务的分支大概有以下几个&#xff1a; master&#xff1a;代码随时可能上线 develop&#xff1a;代码最新 feature/xxx&#xff1a;实际业务开发分支 release/xxx&#xff1a;预发布分支 fix&#xff1a;修复bug分支 过程大概是这样的&#xff1a; 首…...

使用Django自带的后台管理系统进行数据库管理的实例

Django自带的后台管理系统主要用来对数据库进行操作和管理。它是Django框架的一个强大功能&#xff0c;可以让你快速创建一个管理界面&#xff0c;用于管理你的应用程序的数据模型。 使用Django后台管理系统&#xff0c;你可以轻松地进行以下操作&#xff1a; 数据库管理&…...

leetcode解题思路分析(一百四十五)1254 - 1266 题

统计封闭岛屿的数目 二维矩阵 grid 由 0 &#xff08;土地&#xff09;和 1 &#xff08;水&#xff09;组成。岛是由最大的4个方向连通的 0 组成的群&#xff0c;封闭岛是一个 完全 由1包围&#xff08;左、上、右、下&#xff09;的岛。请返回 封闭岛屿 的数目。 BFS或者DFS…...

使用 GORM 连接数据库并实现增删改查操作

步骤 1&#xff1a;安装 GORM 首先&#xff0c;我们需要安装 GORM 包。在终端中运行以下命令&#xff1a; shell go get -u gorm.io/gorm 步骤 2&#xff1a;导入所需的包 在 Go 代码的开头导入以下包&#xff1a; import ("gorm.io/driver/mysql" // 如果你使用…...

kafka集群搭建(Linux环境)

zookeeper搭建&#xff0c;可以搭建集群&#xff0c;也可以单机&#xff08;本地学习&#xff0c;没必要搭建zookeeper集群&#xff0c;单机完全够用了&#xff0c;主要学习的是kafka&#xff09; 1. 首先官网下载zookeeper&#xff1a;Apache ZooKeeper 2. 下载好之后上传到…...

深圳网站建设及优化/北京网络排名优化

MQTT协议笔记之消息流 https://blog.csdn.net/tcjy1000/article/details/61427755 2017年03月11日 20:34:14 tcjy1000 阅读数&#xff1a;716 前言 前面的笔记已把所有消息类型都过了一遍&#xff0c;这里从消息流的角度尝试解读一下。 网络故障 在任何网络环境下&#x…...

健康服务管理中心/昆明网站seo优化

PHP三元运算符的使用方法技巧PHP是一种 HTML 内嵌式的语言&#xff0c;是一种在服务器端执行的嵌入HTML文档的脚本语言&#xff0c;语言的风格有类似于C语言&#xff0c;被广泛地运用。下面是小编为大家搜索整理的PHP三元运算符的使用方法技巧&#xff0c;希望能给大家带来帮助…...

做红包图片的网站/最近韩国电影片

1. 创建标准的表格Code:<table border"2" style"text-align:center"> <!表格的开始标签,定义边框为2,定义文本居中显示><tr> <!第1行开始标签><th>Head…...

南昌网站设计制作/百度平台客服电话是多少

过去一年中&#xff0c;花了很多时间在考虑服务器架构设计方面的问题。看了大量文章、也研究了不少开源项目&#xff0c;眼界倒是开阔了不少&#xff0c;不过回过头来看&#xff0c;对网游架构设计方面的帮助却是不多。老外还是玩儿console game的多&#xff0c;MMO Games方面涉…...

如何做自己网站/关键词排名优化顾问

https://blog.csdn.net/weixin_42642341/article/details/84585253 thrift : 是Facebook公布的一款开源跨语言的RPC框架....

织梦的网站关键词/seo课程培训学校

开始准备看Java NIO的&#xff0c;这篇文章&#xff1a;http://xly1981.iteye.com/blog/1735862 里面提到了这篇文章 http://xmuzyq.iteye.com/blog/783218 同步、异步、阻塞、非阻塞、reactive、proactive等讲的不错。 在高性能的I/O设计中&#xff0c;有两个比较著名的模式Re…...