文章目录

  • 问题引入
  • 如何实现针对对象进行数据监测
  • Vue.set()的使用
  • 如何实现针对数组进行数据监测
  • 数据监测总结

问题引入

我们前面知道只要data中的数据发生变化,那么Vue会重新解析模板更新数据。那么这一定是决定的吗?我们可以看看下面的例子:

我们借用原来的列表案例,对其中的一项进行修改:

如果只是对其中对象的属性进行修改,是奏效的:

<body><!-- 准备好一个容器--><div id="root"><h2>人员列表</h2><button @click="updateMei">更新马冬梅的信息</button><ul><li v-for="(p,index) of persons" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = falseconst vm = new Vue({el:'#root',data:{persons:[{id:'001',name:'马冬梅',age:30,sex:'女'},{id:'002',name:'周冬雨',age:31,sex:'女'},{id:'003',name:'周杰伦',age:18,sex:'男'},{id:'004',name:'温兆伦',age:19,sex:'男'}]},methods: {updateMei(){this.persons[0].name = '马老师' //奏效this.persons[0].age = 50 //奏效this.persons[0].sex = '男' //奏效}}}) </script>
</body>

而如果是对其中的对象整体修改,会发现不奏效:

<body><!-- 准备好一个容器--><div id="root"><h2>人员列表</h2><button @click="updateMei">更新马冬梅的信息</button><ul><li v-for="(p,index) of persons" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = falseconst vm = new Vue({el:'#root',data:{persons:[{id:'001',name:'马冬梅',age:30,sex:'女'},{id:'002',name:'周冬雨',age:31,sex:'女'},{id:'003',name:'周杰伦',age:18,sex:'男'},{id:'004',name:'温兆伦',age:19,sex:'男'}]},methods: {updateMei(){// this.persons[0].name = '马老师' //奏效// this.persons[0].age = 50 //奏效// this.persons[0].sex = '男' //奏效this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'} //不奏效}}}) </script>
</body>

注意:从代码的层面上说其实数据已经被修改,这个可以通过控制台验证验证。但是页面呈现的数据是没有发生变化的。

还会有一个小情况:当点击了修改按钮进行修改之后,再打开Vue的开发者工具会发现数据发生了是修改了之后的(此时页面还是没有改变)。如果我们是打开了Vue开发者工具之后再去进行修改,则Vue开发者工具中的数据仍是修改之前的。

如何实现针对对象进行数据监测

在前面我们说到数据代理的时候,我们当时说Vue会将data中的数据放到_data中去,然后再将_data中的数据进行代理。其实在Vue将data中的数据放到_data之前,Vue对data中的数据进行了加工,而这个加工的步骤就是Vue实现数据监测的关键。

我们可以来验证一下:
对于如下代码(简单的在data中放了两个数据):

<body><!-- 准备好一个容器--><div id="root"><h1>{{name}} </h1><h1>{{province}}</h1></div><script type="text/javascript">Vue.config.productionTip = falseconst vm = new Vue({el:'#root',data:{name:'CSDN',province:'湖北'}}) </script>
</body>

我们可以看到:

vm._data中我们可以发现里面的东西并不完全跟data中的一样,说明确实做了一个加工。而这个加工的内容就是多出来的方法。也就是说Vue为每一个数据添加了get和set方法。

Vue通过这个加工就做成了响应式(图中的reactive其实就是响应的意思)。当我们修改数据的时候,就会执行其setter方法,而这个setter方法里面就有一个调用。他让我们的模板重新解析。

完整流程:
改变数据 => 调用setter => 重新解析模板 =>生成新的虚拟DOM => 新旧DOM对比 =>更新页面

其实我们可以尝试粗略的去实现一下数据监视:

 <body><script type="text/javascript" >let data = {name:'CSDN',address:'北京',}//创建一个监视的实例对象,用于监视data中属性的变化const obs = new Observer(data)      console.log(obs)    //准备一个vm实例对象let vm = {}vm._data = data = obsfunction Observer(obj){//汇总对象中所有的属性形成一个数组const keys = Object.keys(obj)//遍历keys.forEach((k)=>{Object.defineProperty(this,k,{get(){return obj[k]},set(val){console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了`)obj[k] = val}})})}</script></body>

错误写法:
①定时器
②使用如下方式

会造成无限递归

如上的代码只考虑到了一层的情况,也就是说如果出现对象里面还有属性的情况,它发生改变我们是监视不到的。

Vue底层使用递归,它会一直往下找,直到找到某一个东西不再是对象。

例如,如果data是这样的:

         let data = {name:'CSDN',address:'北京',a:{b:1}}

使用如上代码是提供不了b的getter和setter的:

同时Vue里面还使用了数据代理,上面的代码是没有用到的。我们修改数据只能通过_data,而不能直接通过data

如果我们将一个对象藏在了数组里面,Vue也可以把它找出来,并提供相应的getter和setter:

Vue.set()的使用

我们现在有一个需求,人物的性别不能确定,我们想要在后期添加,并且能让页面显示。我们的思路是直接添加任务的性别属性,这样就能显示出来了。但这样真的可以吗?

注意:
Vue不会显示一切为Undefine的值
我们可以衍生一下假设a为对象,b为属性(并不存在的)。
a.b如果a存在b不存在,Vue不会报错,并且不显示undifined
如果直接访问b,b不存在,则Vue会报错。

 <body><!-- 准备好一个容器--><div id="root"><h1>学生信息</h1><h2>姓名:{{student.name}}</h2><h2>性别:{{student.sex}}</h2><h2>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h2><h2>朋友们</h2><ul><li v-for="(f,index) in student.friends" :key="index">{{f.name}}--{{f.age}}</li></ul></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{school:{name:'CSDN ',address:'北京',},student:{name:'tom',age:{rAge:40,sAge:29,},friends:[{name:'jerry',age:35},{name:'tony',age:36}]}},})</script>

然后我们给性别属性赋值:

发现页面并没有发生变化:

我们查看vm可以发现,导致页面没有变化的原因是因为新添加的性别属性根本没有getter和setter:

如果你是先在data中就已经给student定义了sex属性,并且赋值为undefined,那么在数据代理之前的加工步骤中Vue就会为其添加getter和setter,所以再为他赋值是可以改动页面的。

也就是说后添加的属性Vue是不会为其做响应式的。也就是说我们需要响应式的数据最好一开始就在源码中定义好。当然Vue也给我们提供了方法去解决后期添加的数据没有响应式的问题,它就是Vue.set()

我们可以尝试一下:

同时这种方法在vm身上也有一个,它的名字叫$set(),我们也可以试一下:

但是这个方法也有局限性!

例如:

使用这个方法vm以及vm身上的根数据不允许作为target。也就是说只能在data中的对象中才能添加属性,直接在data中加不行。

如何实现针对数组进行数据监测

我们在以上案例的基础上,为student中添加一个属性hobby,其值为数组:

我们接下来将其爱好用列表进行呈现,代码如下:

     <body><!-- 准备好一个容器--><div id="root"><h1>学生信息</h1><h2>姓名:{{student.name}}</h2><h2>性别:{{student.sex}}</h2><h2>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h2><h2>朋友们</h2><ul><li v-for="(f,index) in student.friends" :key="index">{{f.name}}--{{f.age}}</li><li v-for="(h,index) in student.hobby" :key="index">{{h}}</li></ul></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{student:{name:'tom',// sex:undefined,age:{rAge:40,sAge:29,},friends:[{name:'jerry',age:35},{name:'tony',age:36}],hobby:['打游戏','打篮球','做作业']}},})</script>

接下来我们对hobby列表中的其中一项进行修改,发现页面并没有进行更新。

我们查看vm,发现Vue并没有为数组中的每一项配置getter和setter方法:

那么我们怎么能让Vue知道我们修改了数组中的数据呢?

其实Vue中规定了只有使用如下的几种方法对数组进行修改的时候,这种变化才能被监测得到:

  • push() 向数组的末尾添加元素
  • pop() 删除数组的最后一个元素
  • shift() 删除数组的第一个元素
  • unshift() 在数组的开头添加新元素
  • splice() 用于添加和删除元素
  • sort() 对数组进行排序
  • reverse() 对数组进行反转

例如ES6中的filter()方法是不会被监测到的,因为它并不会对原数组造成影响。如果使用filter()我们只需要把副本赋值给原数组即可。(因为数组是由getter和setter方法的,所以可以被监测到,并发生变化)

其实也有其他的方法可以修改并引起响应,我们可以借助于set()方法。例如:

不过这种方法我们用的相对较少。

例如:

那么Vue是如何检测到我们使用了这种办法的呢?这要依赖于一种独特的包装技术 – 包装数组身上的常用的修改数组的方法。

其实我们使用的push,与我们Array.push不是一个方法:


我们原来使用的push是沿着原型链往上找,在原型对象上找到的push。而这里的push是Vue给我们写的push,Vue在这个push方法中为我们做了两件事:

  • 调用正常数组的push方法
  • 重新解析模板,生成虚拟DOM········

数据监测总结

我们将前面使用到的api先进行一个复习。案例如下:点击相应按钮,实现对应功能。

代码如下:

<body><div id="root"><h1>学生信息</h1><button @click="student.age++">年龄+1岁</button> <br/><button @click="addSex()">添加性别属性,默认值:男</button> <br/><button @click="changeSex()">修改性别</button> <br/><button @click.once="addFriend()">在列表首位添加一个朋友</button> <br/><button @click.once="student.friends[0].name = '张三'">修改第一个朋友的名字为:张三</button> <br/><button @click.once="addHobby()">添加一个爱好</button> <br/><button @click.once="$set(student.hobby,0,'开车')">修改第一个爱好为:开车</button> <br/><button @click.once="deleteSmoke()">过滤掉爱好中的抽烟</button> <br/><h3>姓名:{{student.name}}</h3><h3>年龄:{{student.age}}</h3><h3 v-if="student.sex">性别:{{student.sex}}</h3><h3>爱好:</h3><ul><li v-for="(h,index) in student.hobby" :key="index">{{h}}</li></ul><h3>朋友们:</h3><ul><li v-for="(f,index) in student.friends" :key="index">{{f.name}}--{{f.age}}</li></ul></div>
</body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{student:{name:'tom',age:18,hobby:['抽烟','喝酒','烫头'],friends:[{name:'jerry',age:35},{name:'tony',age:36}]}},methods: {addSex(){Vue.set(this.student,'sex','男')},changeSex(){if(this.student.sex == '男') this.student.sex = '女'else this.student.sex = '男'},addFriend(){this.student.friends.unshift({name:'Linda',age:34})},addHobby(){this.student.hobby.push('玩游戏')},deleteSmoke(){let temp =  this.student.hobby.filter((val) => {return val != '抽烟'})this.student.hobby = temp}},})
</script>
</body>

什么是数据劫持?
就是将data中的每一个属性都遍历了一遍,形成getter、setter的形式,这种行为就是数据劫持。
例如我们修改一个属性,会立马被setter劫持到。劫持之后它会正常的帮我们修改数据,另一个就是帮我们重新解析模板。
数据劫持和数据代理都离不开Object.defineProperty()

总结:

Vue核心⑨(数据监测原理)相关推荐

  1. 【重学Vue】数据响应原理真的是双向绑定吗?

    最近 Ant Design Vue 作者 - 唐金州,在某平台开课了,在整个课程中系统的讲述了Vue的开发实战.在第八讲中介绍了Vue双向绑定的问题,这里我整理一些资料客观的分析一下 Vue数据响应原 ...

  2. 【Vue2.0】—Vue监视数据的原理(五)

    [Vue2.0]-Vue监视数据的原理(五) <body><div id="root"><h1>学生的基本信息</h1><bu ...

  3. 探讨Vue 数据监测原理-第四节-Vue.Set() API 介绍应用

    文章目录 探讨一下Vue 数据监测的原理 本次探讨共的第四个章节 第四节:Vue.Set() API 介绍应用 1. 案例 2. 需求1 给学生添加性别 2.1 获取对象添加性别 2.2. 添加响应式 ...

  4. 探讨Vue 数据监测原理-第五节-展开介绍 Vue中监测-【数组】数据的原理

    文章目录 探讨一下Vue 数据监测的原理 本次探讨共的第五个章节 第五节:展开介绍 Vue中监测-[数组]数据的原理 1. 案例 1.1. 页面案例 2. 数组形式|对象形式爱好的区别 2.1. 页面 ...

  5. JavaScript 进阶 31 -- 简单来说下vue.js 数据监测observe的实现的原理

    这篇文章是接上一篇继续来总结一下关于数据监测 observer 实现响应式的原理. 响应式的原理: 只要要数据发生改变,页面视图就发生变化 简单来说 就是一个方法,里面使用for in循环,分别为对象 ...

  6. Vue2.(Vue的指令和数据代理原理,数据监测)

    目录 ​ vue 的两个特性 数据驱动视图: 双向数据绑定: 3.vue的基本使用 vue 指令 1. 内容渲染指令(v-text,{{ }},v-html) 2. 属性绑定指令(v-bind:,简写 ...

  7. Vue教程1 【Vue核心】

    Vue.js 中文文档 (bootcss.com) 使用vue插件 GitHub - vuejs/devtools: ⚙️ Browser devtools extension for debuggi ...

  8. Vue2【尚硅谷--天禹老师】:Vue核心

    目录 1. Vue核心 1.1 介绍与描述 1.2  Vue的特点 1.3 与其他JS框架的关联 1.4 Vue官网的使用指南 2.搭建Vue开发环境 2.1 直接使用 2.2 Hello小案例 2. ...

  9. 1. Vue从入门到精通(第一章 vue核心)

    Vue从入门到精通(第一章 vue核心) 第一章 Vue核心 1. Vue简介 1.1 Vue是什么? 1.2 Vue的作者以及迭代版本 1.3 Vue的特点 2. 搭建Vue开发环境 2.1 安装V ...

最新文章

  1. java locationuitool_Java JavaUI.getLibraryJavadocLocation方法代码示例
  2. 专家解读下一代互联网创新模式,核心技术是根本
  3. node2vec文献出处_详解Node2vec以及优缺点
  4. mysql 删除数据后myd_Windows下的MySQL删除data文件夹后……
  5. PAT甲级1074 Reversing Linked List :[C++题解]反转链表,借用vector
  6. java 静态方法 实例方法的区别_java 静态方法和实例方法的区别
  7. docker Registry镜像仓库
  8. matlab程序svm四等级分类,支持向量机(SVM)多分类matlab程序代码
  9. 【jenkins】jenkins+maven+gitlab+testng,jenkins配置
  10. Centos7 下Jenkins 安装
  11. ibmt41 安装linux系统,哥我决意为IBM T41 装WIN7的决心已经到了全人类都无法阻止的地步!...
  12. 文字表情 emoji 解析大全
  13. 合并两个有序表(C语言)
  14. 超媒体是什么?Hypermedia(一种采用非线性网状结构对块状多媒体信息(包括文本、图像、视频等)进行组织和管理的技术)
  15. ubuntu下打开nsg2的命令
  16. Linux命令--tac(倒序查看文件所有内容)
  17. http接口测试:了解协议、请求方法、响应状态码
  18. 如何安装华为路由器模拟环境ENSP
  19. 为什么程序员要学linux?
  20. 论文阅读:Detecting Visual Relationships with Deep Relational Networks

热门文章

  1. 域服务器 文件服务器,域服务器文件服务器
  2. 每天5分钟玩转Kubernetes | Cluster IP底层实现
  3. java采集控制台日志
  4. c语言优秀教案,C语言优秀教案.doc
  5. c语言第二单元测试,知到计算机程序设计C语言第二单元章节测试答案
  6. 我又有一位程序员朋友成了自由职业者
  7. 简单几步实现网络音乐播放器(Python爬虫版百度FM)
  8. 6.进程通信 无名管道 有名管道
  9. 2020中级计算机工程师,2020年上半年中级网络工程师报考详解
  10. 7-105 sdut-C语言实验——三个数排序7-106 sdut-C语言实验——模拟计算器7-107 sdut-C语言实验——找中间数