Vue2与Vue3响应式的详解与比对

news2024/11/15 12:50:24

目录

  • 前言
  • 一,响应式的理解
    • 1.1 Mvvm模式的复习
    • 1.2 什么是响应式
  • 二, Vue2中响应式的应用
  • 三,Vue2响应式的原理及实现
    • 3.1 数据代理
    • 3.2 数据代理的原理
    • 3.3 数据劫持
  • 四,Vue3中响应式的应用
    • 3.1 ref与reactive
    • 3.2 ref函数的使用讲解
    • 3.3 为什么要出现reactive及其用法
  • 五, Vue3响应式的原理及实现
    • 5.1 底层原理
    • 5.2 window上内置对象Reflect
    • 5.3 Vue3响应式原理的总结
  • 后记

前言

在我们学习Vue响应式之前,我们需要了解,Vue的响应式是什么,Vue响应式的怎么用,以及其内部深层次的原理。

一,响应式的理解

1.1 Mvvm模式的复习

Vue响应式的响应式又可以称之为数据双向绑定。双向绑定来源于有关Vue设计的Mvvm模式,这里对其进行简单复习。
在这里插入图片描述

Mvvm拆解:
M:Model:模型,对应data中的数据;
View:模板,页面结构,可以理解为用户界面,是由真实的Dom结构构成;
VM:视图模型,对应的是Vue的实例对象。
可以根据下面这张图片进行理解:
在这里插入图片描述
其中,Model中的数据通过Vm传递给View,在页面中可以反映出name的值为‘巧克力小猫猿’;同样,如果改变了View中name的值(通过input的value),Model中的数据也会发生改变。

1.2 什么是响应式

响应式就是在数据变化时可以被检测并对这种变化做出响应的机制响应式,简单说就是用户更改数据(Data)时,视图可以自动刷新,页面UI能够响应数据变化。

结合Mvvm,如果我们要实现响应式,其实就是实现Model到View的同步改变,当数据发生变化时,视图也发生变化。

二, Vue2中响应式的应用

Vue2中用到响应式的地方很多,用法简单粗暴,比如模板语法。只要data中存在数据,就可以通过模板语法进行使用:

<template>
	<div>
		我是{{ name }}
	</div>
</template>
<script>
	export default {
		data() {
		return {
			name: '巧克力小猫猿'
			}
		}
	}
</script>

页面中即可显示data中的数据:
在这里插入图片描述

三,Vue2响应式的原理及实现

3.1 数据代理

数据代理的定义:通过一个对象代理对另一个对象中的属性的操作(读/写),就是数据代理。

Vue2的响应式原理离不开数据代理。数据代理是什么意思?这里用一个例子来解释。如下,person是一个对象:

        let person = {
            name: '张三',
            age: 18
        }

我们可以试着在浏览器控制台来读取数据,修改数据。但是,在vue2中,是vm来管理这些数据。也就是说,我们不直接操纵person,而是通过vm(Vue的实例对象)来管理person中的数据。这种情况就叫做数据代理。

3.2 数据代理的原理

说到数据代理,我们需要了解Object.defineProperty。通过它我们可是实现一个对象去管理另一个对象中的数据。

这里介绍一下它的语法:

Objtec.defineProperty(参数一, 参数二, 参数三)

参数一:需要添加或设置属性的对象
参数二:需要添加或设置属性的属性名
参数三:配置对象

我们来看一段实用的代码:

<body>
    <script>
        let person = {
            name: '张三',
            age: 18
        }

        //模拟vue2中实现响应式
        let p = {}
        Object.defineProperty(p, 'name', {
            get() {
                //有人读取name时调用
                return person.name
            },
            set(value) {
                //有人修改name时调用
                console.log('有人修改了name属性,我发现了,我要去更新界面')
                person.name = value
            }
        })
    </script>
</body>

在这一段代码中,我们定义了一个空对象p,并用p管理person中的name。在这段代码中,get是指读取,会在读取参数二name时调用,set是指更改,会在更改参数二name时被调用。可以看出,我们在用新建立的对象p去读取person中的name,或者更改person中的name时,会分别调用get和set。

这样,就做到了用对象p去修改person中的内容。

在vue2中,也是通过这样的原理,用Vue的实例对象vm来管理data中的数据。

3.3 数据劫持

在弄懂上述的原理后,我来讲解一个概念:数据劫持。以上面的例子为主来说明,如果name被读取 ,会被p监测到并调用get,如果name被修改,会被p检测到并调用set。这样在中间劫取数据并进行操作的情况就叫做数据劫持。

现在我们来看一下数据劫持的概念:指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。

四,Vue3中响应式的应用

3.1 ref与reactive

在Vue3中,如果要实现响应式,离不开ref和reactive。Vue3中的数据书写不像Vue2一样使用data,而是存储在setup函数中。setup函数中可以放置普通类型的数据,也可以放置对象类型的数据。最后需要暴露的数据被setup函数return出来即可。

但是在数据的书写中,如果要实现响应式,就需要ref和reactive。如下代码,这是一个setup函数。其中,==普通类型的数据用ref函数,对象类型的数据用reactive函数。==这也是Vue3响应式的基本用法。

这里注意,ref和reactive都需要提前引入。

import { ref, reactive } from 'vue'
  setup() {
    //数据
    var name = ref('zxd')
    var age = ref(18)
    // var sex = '女'
    var sex = ref('女');
    //对象类型数据
    let job =reactive({
      type: "前端工程师",
      salary: '30k',
      a: {
        b: {
          c:666
        }
      }
    })    
    return {name, age, sex, job}
    }             

3.2 ref函数的使用讲解

我们先用ref包裹一个数据,并看一下,该数据被ref包裹后是什么,请看如下代码:

<template>
  <h1>姓名{{ name }}</h1>
  <button @click="look">点击查看name</button>
</template>

<script>
import { ref, reactive } from 'vue'
export default {
  setup() {
    let name = ref('巧克力小猫猿')
    function look() {
       console.log(name)
    }
    return {
      name, look
    }
  }
}
</script>在这里插入图片描述


点击查看name:在这里插入图片描述
可以看出name是一个RefImpt对象。这里解释下RefImpt是什么 :RefImpt全程,reference(引用)implement(实现)对象,所以其称之为引用实现对象的实例对象,简称引用对象。

现在提一个需求,我们需要修改name的value改成巧克力,如下代码如果我们直接更改name:

<template>
  <h1>姓名{{ name }}</h1>
  <button @click="look">点击查看name</button>
  <button @click="change">修改name的名字</button>
</template>

<script>
import { ref, reactive } from 'vue'
export default {
  setup() {
    let name = ref('巧克力小猫猿')
    function look() {
       console.log(name)
    }
    function change() {
      name = '小猫'
      console.log(name)
    }
    return {
      name, look, change
    }
  }
}
</script>

运行结果是name被改变但没有响应式:
在这里插入图片描述
我们来试一下name.value进行修改,则可以成功被修改:
在这里插入图片描述
所以,在使用ref的时候,如果是在模板语法中,不用写value,但是在JavaScript中操作,value不可以省略。

3.3 为什么要出现reactive及其用法

为什么对象类型要使用reactive?这里我们可以先用ref试一下,看看能不能实现功能。

如下代码:

<template>
  <h1>姓名{{ name }}</h1>
  <h1>工作{{ job.type }}</h1>
  <h1>薪水{{ job.salary }}</h1>
  <button @click="look">点击查看name</button>
  <button @click="change">修改name的名字</button>
  <button @click="look2">查看对象</button>
</template>

<script>
import { ref, reactive } from 'vue'
export default {
  setup() {
    let name = ref('巧克力小猫猿')
    let job = ref({
      type: '前端工程师',
      salary: '60k'
    })
    function look() {
       console.log(name)
    }
    function change() {
      name.value = '小猫'
      console.log(name)
    }
    function look2() {
      console.log(job)
    }
    return {
      name, look, change, job, look2
    }
  }
}
</script>

我们添加了一个job对象并打印:
在这里插入图片描述
可以看出依旧是一个引用对象,到目前为止都没有出现什么问题。比较明显的区别是,Value处显示的是一个Proxy,我们目前不知道是什么,但是可以通过打印来观察:
在这里插入图片描述
可以看到,Proxy也是一个对象:
在这里插入图片描述
那么,如果我们要修改对象中的内容,可以通过job.value.type,job.value.salary来修改:
在这里插入图片描述
在这里插入图片描述
可以修改,但是非常麻烦,每一次都要写一个value。那么我们如何简化,就出现了reactive。reactive和ref用法基本相同:

<template>
  <h1>姓名{{ name }}</h1>
  <h1>工作{{ job.type }}</h1>
  <h1>薪水{{ job.salary }}</h1>
  <button @click="look">点击查看name</button>
  <button @click="change">修改name的名字</button>
  <button @click="look2">查看对象</button>
  <button @click="change2">修改薪水</button>
</template>

<script>
import { ref, reactive } from 'vue'
export default {
  setup() {
    let name = ref('巧克力小猫猿')
    let job = reactive({
      type: '前端工程师',
      salary: '60k'
    })
    function look() {
       console.log(name)
    }
    function change() {
      name.value = '小猫'
      console.log(name)
    }
    function look2() {
      console.log(job)
      console.log(job.value)
    }
    function change2() {
      job.salary = '90k'
    }
    return {
      name, look, change, job, look2, change2
    }
  }
}
</script>

但是我们不需要写value就可以更改成功:
在这里插入图片描述
另外,reactive相比于ref有一个好处,就是reactive响应数据是深层次的

比如这样一个对象:在这里插入图片描述
我们需要修改c:

<template>
  <h1>姓名{{ name }}</h1>
  <h1>工作{{ job.type }}</h1>
  <h1>薪水{{ job.salary }}</h1>
  <button @click="look">点击查看name</button>
  <button @click="change">修改name的名字</button>
  <button @click="look2">查看对象</button>
  <button @click="change2">修改薪水</button>
  <button @click="changec">修改c</button> 
  {{ job.a.b.c }}
</template>

<script>
import { ref, reactive } from 'vue'
export default {
  setup() {
    let name = ref('巧克力小猫猿')
    let job = reactive({
      type: '前端工程师',
      salary: '60k',
      a: {
        b: {
          c: 1
        }
      }
    })
    function look() {
       console.log(name)
    }
    function change() {
      name.value = '小猫'
      console.log(name)
    }
    function look2() {
      console.log(job)
      console.log(job.value)
    }
    function change2() {
      job.salary = '90k'
    }
    function changec() {
      job.a.b.c = 2
    }
    return {
      name, look, change, job, look2, change2, changec
    }
  }
}
</script>

非常的简便,直接job.a.b.c即可:
在这里插入图片描述

五, Vue3响应式的原理及实现

5.1 底层原理

Vue3响应式的底层依旧是通过数据代理和数据劫持来实现vm对数据的管理。但是方式与Vue2有所不同。我们先看下基本结构,依旧是一个person对象:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    巧克力小猫猿
    <script>
        let person = {
            name: '张三',
            age: 18
        }
    </script>
</body>
</html>

这里需要给读者介绍以下Proxy函数。Proxy函数是window身上内置的,使用方式:

const p = new Proxy(person, {})

其中,p是代理对象,Proxy内部,person是源数据(需要管理的数据),后面的则是数据劫持中捕获数据的修改。

请看以下使用:

        const p = new Proxy(person, {
            get(target, propName) {
                console.log(`有人读取了p身上的${propName}属性`)
                return target[propName]
            },
            set(target, propName, value) {
                console.log(`有人修改了p身上${propName}`)
                target[propName] = value 
            },
            deleteProperty(target, propName) {
                return delete target[propName]
            }
        });

这里,依旧有get,set,以及山粗deleteProperty。通过p对源数据person进行管理。这里要注意,target是指源数据,而propName是指读的谁。

至于这里的return为什么这么写,这是Es6的写法,因为propName是target中的一个变量,而不是字符串。这里的propName和target是类似形参的,而不是实参。

5.2 window上内置对象Reflect

Reflect对象可以从对象中读数据。例如Reflect.get(‘obj’, ‘a’)意思是从obj中读a属性,那么我们的return就无需刚刚那样写了,可以都使用Reflect:

        const p = new Proxy(person, {
            get(target, propName) {
                console.log(`有人读取了p身上的${propName}属性`)
                // return target[propName]
                 return Reflect.get(target, propName)
            },
            set(target, propName, value) {
                console.log(`有人修改了p身上${propName}`)
                // target[propName] = value 
                return Reflect.set(target, proName, value)
            },
            deleteProperty(target, propName) {
                // return delete target[propName]
                return Reflect.deleteProperty(target.propName)
            }
        });
        

以上就是Vue3的响应式原理。

5.3 Vue3响应式原理的总结

1.通过Proxy进行代理,拦截对象中任意属性的变化,包括属性的读写,添加,删除。

2.通过Reflect反射对源对象进行操作。

reactive与ref的对比:
1.原理上,ref通过Vue2中的响应式原理get与set实现响应式;reactive通过使用Proxy来实现响应式,并通过Reflect操作源对象内部数据。

2.使用角度:ref定义的数据操作需要value,读取无需value,而reactive定义的数据均不需要value。

后记

本篇博客讲解了Vue2与Vue3响应式的用法,原理与对比。如果有什么问题,期望大家批评指正。

最后,欢迎关注,希望给读者带来更好的文章!

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

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

相关文章

Anaconda配置Python新版本tensorflow库(CPU、GPU通用)的方法

本文介绍在Anaconda环境中&#xff0c;下载并配置Python中机器学习、深度学习常用的新版tensorflow库的方法。 在之前的两篇文章基于Python TensorFlow Estimator的深度学习回归与分类代码——DNNRegressor&#xff08;https://blog.csdn.net/zhebushibiaoshifu/article/detail…

圣杯布局的实现方式

1.什么是圣杯布局&#xff1f; 左右盒子固定&#xff0c;中间盒子自适应 2.实现方式 &#xff08;1&#xff09;flex布局 思路&#xff1a;左右盒子给固定的宽高&#xff0c;中间盒子flex:1 <!DOCTYPE html> <html lang"en"> <head> <met…

JavaSE学习进阶 day1_01 static关键字和静态代码块的使用

好的现在我们进入进阶部分的学习&#xff0c;看一张版图&#xff1a; 前面我们已经学习完基础班的内容了&#xff0c;现在我们已经来到了第二板块——基础进阶&#xff0c;这部分内容就不是那么容易了。学完第二板块&#xff0c;慢慢就在向java程序员靠拢了。 面向对象进阶部分…

入门力扣自学笔记240 C++ (题目编号:2373)

2373. 矩阵中的局部最大值 题目&#xff1a; 给你一个大小为 n x n 的整数矩阵 grid 。 生成一个大小为 (n - 2) x (n - 2) 的整数矩阵 maxLocal &#xff0c;并满足&#xff1a; maxLocal[i][j] 等于 grid 中以 i 1 行和 j 1 列为中心的 3 x 3 矩阵中的 最大值 。 换句…

学习渗透测试,考CISP-PTE还是考NISP-PT证书呢?

其实两者都可以&#xff0c;但是要看考生的实际需求&#xff01; 为什么说两者都可以&#xff1f; 两个证书都由中国信息安全测评中心颁发&#xff0c;CISP-PTE&#xff08;注册信息安全渗透测试工程师&#xff09;,NISP-PT&#xff08;国家信息安全水平考试渗透测试工程师),…

月薪30k测试岗技术要求,简简单单,你学废了吗?

如果还只会点点点&#xff0c;那么可以往自动化测试的方向发展&#xff0c;然后再往测试开发的方向发展&#xff0c;做一个测试开发的工程师&#xff0c;这样薪资是非常可观的。当然过程中需要学习很多的知识&#xff0c;比如&#xff1a;编程语言&#xff0c;自动化测试框架&a…

2023年工程师中级和高级有什么区别,他们评审的要求有哪些不同?

2023年工程师中级和高级有什么区别&#xff0c;他们评审的要求有哪些不同&#xff1f; 职称主要分为初、中、高三个等级。在大部分地区都是逐级申报的&#xff0c;先初级再中级最后高级。不少人都想直接评中级&#xff0c;这是不可行的的&#xff0c;除少数地区破格来说&#x…

软件测试工程师该怎么做自己的职业规划呢

软件测试需求量不仅稳健&#xff0c;还会加大 疫情前&#xff0c;人们的“吃、穿、住、用、行”方方面面都有对应APP软件。疫情后&#xff0c;复工最快&#xff0c;最迅速的企业也都是通过互联网技术实现。 过去&#xff0c;互联网技术只是让某些企业活的好。未来&#xff0c…

面经-Spring框架相关

面试题 Spring、SpringMVC、SpringBoot的区别 Spring是轻量级的开发框架&#xff0c;主要提供了IOC依赖注入容器和AOP面向切面编程的功能。 SpringMVC是基于Spring的一个用来解决Web开发的问题&#xff0c;主要处理web开发中路径映射和视图渲染等 SpringBoot是融合了Spring…

关于算力的未来,新一代PowerEdge告诉你答案

从ChatGPT等大模型海量参数的训练&#xff0c;自动驾驶领域感知模型的训练与仿真&#xff0c;到蛋白质机构预测、流体力学仿真等AIScience&#xff0c;再到矿山、交通、能源等部署广泛的边缘计算设备……如今&#xff0c;我们愈发确切地认识到&#xff0c;算力在数字经济时代不…

Ubuntu系统设置开机自启

在测试国产操作系统&#xff1a;银河麒麟、UOS统信机器的过程中&#xff0c;发现开机不自启&#xff0c;总结以下几种方式实现自启 一.rc.local rc.local脚本是一个Ubuntu开机后自动执行的脚本&#xff0c;可以在脚本内添加行指令&#xff0c;该脚本位于/etc/路径下&#xff…

【Pytorch】Pytorch深度学习实战教程:超分辨率重建AI与环境搭建

一、基础开发环境搭建 1&#xff09;cuda安装 需要根据自己的显卡的型号选择支持的CUDA版本 显卡驱动查看&#xff1a; 鼠标右键 CUDA安装版本查看&#xff1a;https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html 注意看自己的电脑配置&#xff0c;我的…

基于麻雀算法改进的BP神经网络坑基监测,BP神经网络详细原理,

目标 背影 BP神经网络的原理 BP神经网络的定义 BP神经网络的基本结构 BP神经网络的神经元 BP神经网络的激活函数&#xff0c; BP神经网络的传递函数 麻雀算法原理 麻雀算法主要参数 麻雀算法流程图 麻雀算法优化测试函数代码 基于麻雀算法改进的BP神经网络坑基监测 数据 matlab…

matlab-数据和数据运算

学习视频基本数据类型1.1 整型与浮点型在matlab中同样有8、16、32、64bit的数据大小之分&#xff0c;同时也可以叠加signed(有符号)和unsigned(无符号)的区别&#xff0c;默认数据类型为double(双精度浮点型)参考其他博客的详述1.2 复数还有一些其他常用的函数方法&#xff1a;…

STM32之定时器

定时器软件定时缺点&#xff1a;不精确&#xff0c;占用CPU资源定时器工作原理使用精准的时基&#xff0c;通过硬件的方式&#xff0c;实现定时功能。定时器的核心是计数器。通用定时器框图该框图主要分成四部分&#xff1a;时钟产生器、时基单元、输入捕获、输出比较时钟产生器…

springboot通过aop实现全局日志(是否自定义注解都可以)

内容参考自以下两个链接1、springboot中使用AOP切面完成全局日志_aop全局日志_邹飞鸣的博客-CSDN博客使用AOP记录日志_aop日志_trusause的博客-CSDN博客第一个链接思路很清晰,讲的也很详细,第二个链接讲了自定义注解为了便于自己理解做了以下整理目录 1.aspectj基本概念 2.添加…

闪光桐人の实习日记(2023年2月27日-3月3日)

前往闪闪の小窝以获得更好的阅读和评论体验 文章目录2023年3月2日&#xff08;测试流程&#xff09;为什么是什么如何进行2023年3月1日&#xff08;消息队列MQ&#xff09;什么是消息队列为什么要使用消息队列消息队列&#xff08;kafka&#xff09;的优势关键信息SpringBoot整…

LeetCode题目笔记——448. 找到所有数组中消失的数字

文章目录题目描述题目链接题目难度——简单方法一&#xff1a;使用额外空间&#xff0c;字典代码/Python代码/C方法二&#xff1a;进阶&#xff0c;原地修改代码/C代码/C总结题目描述 这好像是一到经典的面试题 给你一个含 n 个整数的数组 nums &#xff0c;其中 nums[i] 在区间…

遮挡检测--基于角度的遮挡检测方法

文章目录1基于角度的遮挡检测方法2遮挡检测遍历方法2.1方法1--自适应径向扫描方法2.2方法2--螺旋扫描法参考1基于角度的遮挡检测方法 在基于角度的方法中&#xff0c;通过依次分析DSM中沿径向方向的投影光线的角度来识别遮挡。定义α\alphaα角&#xff1a;DSM三维点与相机中心…

解决Windows虚拟机启动资源占用过多

由于虚拟机中的Windows一般是指定版本的&#xff0c;创建后&#xff0c;其自身仍在不断自动更新&#xff0c;因此这里我们禁用两个服务&#xff0c;以后启动Windows之后就不会占用太多资源了&#xff1a; 禁用setuphost.exe禁用.net runtime optimization 文章目录1. 禁用setu…