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

VUE 学习笔记6 vue数据监测原理

Vue监测数据改变的原理

数据:指的是存在data中的数据。

如果我们修改了data中存储的数据,Vue会监测到这种修改,并且把修改反应到页面上。

Vue的这种监测是通过Vue上默认的监视实现的。与watch不同,watch是Vue提供给开发者使用的方法。但不管是默认监视,还是watch,背后的原理都类似。

了解Vue的监视原理是很重要的,如果不知道,当自己写的某种数据修改不能被Vue识别到,就很难去分析成因。

一种Vue无法监视到数据修改的情况:

如果用访问下标的方法替换数组中某个对象元素,把整个元素都替换成新的,虽然后台数据已经修改了,但Vue无法检测到这种修改,因此页面的数据也无法改变。

而且,有时候,页面上的数据没有改变,但通过控制台输出修改后的数据,再打开开发者工具,开发者工具中的数据会发生改变,但页面上的内容仍然不变,这种错乱的现象令人迷惑。

<body><div id="root"><button @click="changeData">change</button><ul><li v-for="(data,index) in listData" :key="data.id">{{ data.name }}:{{data.age}} -- {{index}}</li></ul></div><style>.styleBackgroundColor{background-color: aqua;}.styleContent{width:300px;height: 200px;}.styleBorder{border: 2px black solid;}</style><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>let vm = new Vue({el: '#root',data() {return {listData:[{name:'catcat',age:5,id:'001'},{name:'dogcat',age:8,id:'002'},{name:'QAQ',age:15,id:'003'},{name:'QAQTAT',age:15,id:'004'},{name:'123',age:15,id:'005'},],}},methods:{changeData(){this.listData[0] = {name:'test',age:'changed',id:'001'};}}})</script>
</body>

在这个例子中,页面上显示原始数组信息,当点击change按钮时,修改数组中的第一条数据,但这时令人迷惑的情况发生了,页面上的数据并不会发生改变,但在控制台打印listData数组,后台的数据已经被更新,这种更新并没能传递到页面上:

这与Vue的监测机制相关。

Vue如何监测对象数据的改变

Vue会对data中的数据加工,生成_data,_data中为每个属性都添加了set和get,当data中数据改变时,会触发数据对应的set函数,set函数的执行代表有数据发生变化,会引起Vue模版的重新解析,Vue模版会使用新的数值解析指令,最终在页面上显示出新的数据。

要对数据进行监测,要靠Observer函数进行实现,Observer函数是一个构造函数,能够创建一个监视的实例对象。

在Observer构造函数内部,首先会汇总对象中所有属性,并形成数组。然后会遍历这个数组,为Observer实例对象添加所有属性,对每个属性设置get和set,在get函数内部,返回被监视data的属性值,在set函数内部,把被监视data的属性值修改掉。

简单的模拟代码如下(实际vue底层代码要更复杂和完善一些):

    <script>let data = {name: 'catcat',age: 10,}function Observer(obj){//获取数据中的所有属性let keys = Object.keys(obj);for(const k of keys){//把所有属性添加到Observer实例上,并且增加set和get方法Object.defineProperty( this, k , {get(){return obj[k]; },set(value){console.log('发现修改,重新进行模版解析等行为');obj[k] = value;}} )}}//创建监听实例let obs = new Observer(data);const vm = {};vm._data = data = obs;</script>

对于这段代码,当前对data数据只考虑了一层数值,当data数值是对象时,当前的代码对对象并不生效。但Vue会对对象及对象内嵌套的对象进行递归,为每层的数据都添加get和set,直到没有层级结构。Vue对藏在数组中的对象,也都为对象的属性赋予了get和set。

对于下列这种数据:

    <script>const vm = new Vue({data:{a:{f:100,b:{g:'123123',c:{d:1,e:2,}}},list:[{name:'a',age:1,},{name:'n',age:2,}],},})</script>

可以看到a以及内层的bf,b内层的cg,c内层的de,上面都有get和set。

对于list,list内部的对象属性也有get和set。

Vue.set

对于data中的数据,在vue实例创建阶段配置好的数据,是响应式的,但对于后加入的数据,由于没有set和get,并不是响应式的。

比如:

<body><div id="id"><h2>{{a.cat}}</h2></div><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>const vm = new Vue({el:'#id',data:{a:{f:100,b:{g:'123123',c:{d:1,e:2,}}}},})</script>
</body>

对于这段代码,页面上显示了data中的a.cat,如果后续给a添加了cat属性,页面上还是无法显示出cat,因为后添加的cat并不是响应式的,页面无法监测到后台新加了数据。

如果希望后添加的数据也有响应式,需要使用Vue上提供的一个api。

Vue.set(target 往谁的身上追加属性,key 要追加的属性名 字符串格式,value 追加的属性值)

除了Vue.set,Vue实例对象上也有一个相同功能的方法,vm.$set。语法是一模一样的。

要找到追加属性的位置,可以不用vm._data.a,用vm.a也可以。

<body><div id="id"><button @click="add">addData</button><h2>{{a.cat}}</h2></div><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>const vm = new Vue({el:'#id',data:{a:{f:100,}},methods:{add(){Vue.set(this.a,'cat','ttt');//this.$set(this.a,'cat','ttt');}}})</script>
</body>

不过,set Api也有一些缺点。set Api只能给data中某一个对象追加属性,不能够给data追加属性。vm和vm._data不能作为target。

Vue如何监测数组数据的改变:

对于数组数据,vue不会为数组内部的元素添加set和get。Vue对数组其实也有监视机制,只不过不是通过set和get进行,因此通过下标修改数组元素,Vue不会监听到这种修改。

<body><div id="id"><button @click="change">change</button><h2 v-for="data in list">{{data}}</h2></div><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>const vm = new Vue({el:'#id',data:{list:['123',{name:'a',age:2},100]},methods:{change(){console.log('change');this.list[0] = 'aa';}}})</script>
</body>

页面上有change按钮,当点击按钮的时候修改数组第一条数据,但点击按钮之后,页面上的数据并不会发生变化。

当对数组数据使用push pop shift unshift splice sort reverse这些能够修改数组本身的方法时,Vue才能监测到

            methods:{change(){console.log('change');this.list.shift();this.list.unshift('aa');}}

对于不修改数组本身的方法,比如filter,只使用filter也是无法被vue监测到的,需要把filter的返回值赋值给整个数组,vue才能监测到这种修改。

因此,回到最开始的问题,为什么把数组第一条数据用下标修改之后,页面上的数据无法进行更新,是因为vue无法监测到对下标访问的修改。

vue是如何监测到对数组使用了修改数组本身的方法:vue使用了包装的手段,也就是说,在vue中调用数组的push等方法,这时使用的并不是Arrary原型对象上的push,而是vue自己写的push。在vue的push中,一开始还是调用了array原型上的push,然后,vue会重新解析模版,生成新的虚拟DOM,diff算法,然后更新页面。

除了使用push pop等方法修改数组,能实现响应式,用Vue.set vm.$set Api也可以响应式修改数组

http://www.dtcms.com/a/297754.html

相关文章:

  • 设计模式十:单件模式 (Singleton Pattern)
  • 空间信息与数字技术专业能从事什么工作?
  • 【LeetCode数据结构】二叉树的应用(二)——二叉树的前序遍历问题、二叉树的中序遍历问题、二叉树的后序遍历问题详解
  • uniapp创建vue3+ts+pinia+sass项目
  • 2025年RISC-V中国峰会 主要内容
  • 绘图库 Matplotlib Search
  • RISC-V VP、Gem5、Spike
  • 恋爱时间倒计时网页设计与实现方案
  • 借助Aspose.HTML控件,在 Python 中将 SVG 转换为 PDF
  • Vue nextTick
  • 基于超176k铭文数据,谷歌DeepMind发布Aeneas,首次实现古罗马铭文的任意长度修复
  • MySQL存储引擎深度解析与实战指南
  • Java面试题及详细答案120道之(001-020)
  • JAVA_FIFTEEN_异常
  • LeetCode 233:数字 1 的个数
  • Zero-Shot TrackingT0:对象分割+运动感知记——当“切万物”武士学会运动记忆,目标跟踪稳如老狗
  • 力扣面试150题--寻找旋转排序数组中的最小值
  • 互联网金融项目实战(大数据Hadoop hive)
  • 代码随想录算法训练营第五十三天|图论part4
  • Hive【Hive架构及工作原理】
  • Hive-vscode-snippets
  • 微信小程序文件下载与预览功能实现详解
  • nacos安装
  • SpringBoot配置多数据源多数据库
  • Androidstudio 上传当前module 或本地jar包到maven服务器。
  • 线性代数 上
  • Java 大视界 -- 基于 Java 的大数据分布式存储在工业互联网数据管理与边缘计算协同中的创新实践(364)
  • 从入门到进阶:JavaScript 学习之路与实战技巧
  • Nginx 安装与 HTTPS 配置指南:使用 OpenSSL 搭建安全 Web 服务器
  • Django集成Swagger全指南:两种实现方案详解