Vue核心⑨(数据监测原理)
文章目录
- 问题引入
- 如何实现针对对象进行数据监测
- 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核心⑨(数据监测原理)相关推荐
- 【重学Vue】数据响应原理真的是双向绑定吗?
最近 Ant Design Vue 作者 - 唐金州,在某平台开课了,在整个课程中系统的讲述了Vue的开发实战.在第八讲中介绍了Vue双向绑定的问题,这里我整理一些资料客观的分析一下 Vue数据响应原 ...
- 【Vue2.0】—Vue监视数据的原理(五)
[Vue2.0]-Vue监视数据的原理(五) <body><div id="root"><h1>学生的基本信息</h1><bu ...
- 探讨Vue 数据监测原理-第四节-Vue.Set() API 介绍应用
文章目录 探讨一下Vue 数据监测的原理 本次探讨共的第四个章节 第四节:Vue.Set() API 介绍应用 1. 案例 2. 需求1 给学生添加性别 2.1 获取对象添加性别 2.2. 添加响应式 ...
- 探讨Vue 数据监测原理-第五节-展开介绍 Vue中监测-【数组】数据的原理
文章目录 探讨一下Vue 数据监测的原理 本次探讨共的第五个章节 第五节:展开介绍 Vue中监测-[数组]数据的原理 1. 案例 1.1. 页面案例 2. 数组形式|对象形式爱好的区别 2.1. 页面 ...
- JavaScript 进阶 31 -- 简单来说下vue.js 数据监测observe的实现的原理
这篇文章是接上一篇继续来总结一下关于数据监测 observer 实现响应式的原理. 响应式的原理: 只要要数据发生改变,页面视图就发生变化 简单来说 就是一个方法,里面使用for in循环,分别为对象 ...
- Vue2.(Vue的指令和数据代理原理,数据监测)
目录 vue 的两个特性 数据驱动视图: 双向数据绑定: 3.vue的基本使用 vue 指令 1. 内容渲染指令(v-text,{{ }},v-html) 2. 属性绑定指令(v-bind:,简写 ...
- Vue教程1 【Vue核心】
Vue.js 中文文档 (bootcss.com) 使用vue插件 GitHub - vuejs/devtools: ⚙️ Browser devtools extension for debuggi ...
- Vue2【尚硅谷--天禹老师】:Vue核心
目录 1. Vue核心 1.1 介绍与描述 1.2 Vue的特点 1.3 与其他JS框架的关联 1.4 Vue官网的使用指南 2.搭建Vue开发环境 2.1 直接使用 2.2 Hello小案例 2. ...
- 1. Vue从入门到精通(第一章 vue核心)
Vue从入门到精通(第一章 vue核心) 第一章 Vue核心 1. Vue简介 1.1 Vue是什么? 1.2 Vue的作者以及迭代版本 1.3 Vue的特点 2. 搭建Vue开发环境 2.1 安装V ...
最新文章
- java locationuitool_Java JavaUI.getLibraryJavadocLocation方法代码示例
- 专家解读下一代互联网创新模式,核心技术是根本
- node2vec文献出处_详解Node2vec以及优缺点
- mysql 删除数据后myd_Windows下的MySQL删除data文件夹后……
- PAT甲级1074 Reversing Linked List :[C++题解]反转链表,借用vector
- java 静态方法 实例方法的区别_java 静态方法和实例方法的区别
- docker Registry镜像仓库
- matlab程序svm四等级分类,支持向量机(SVM)多分类matlab程序代码
- 【jenkins】jenkins+maven+gitlab+testng,jenkins配置
- Centos7 下Jenkins 安装
- ibmt41 安装linux系统,哥我决意为IBM T41 装WIN7的决心已经到了全人类都无法阻止的地步!...
- 文字表情 emoji 解析大全
- 合并两个有序表(C语言)
- 超媒体是什么?Hypermedia(一种采用非线性网状结构对块状多媒体信息(包括文本、图像、视频等)进行组织和管理的技术)
- ubuntu下打开nsg2的命令
- Linux命令--tac(倒序查看文件所有内容)
- http接口测试:了解协议、请求方法、响应状态码
- 如何安装华为路由器模拟环境ENSP
- 为什么程序员要学linux?
- 论文阅读:Detecting Visual Relationships with Deep Relational Networks
热门文章
- 域服务器 文件服务器,域服务器文件服务器
- 每天5分钟玩转Kubernetes | Cluster IP底层实现
- java采集控制台日志
- c语言优秀教案,C语言优秀教案.doc
- c语言第二单元测试,知到计算机程序设计C语言第二单元章节测试答案
- 我又有一位程序员朋友成了自由职业者
- 简单几步实现网络音乐播放器(Python爬虫版百度FM)
- 6.进程通信 无名管道 有名管道
- 2020中级计算机工程师,2020年上半年中级网络工程师报考详解
- 7-105 sdut-C语言实验——三个数排序7-106 sdut-C语言实验——模拟计算器7-107 sdut-C语言实验——找中间数