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

VUE2 学习笔记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等方法,这时使用的并不是Array原型对象上的push,而是vue自己写的push。在vue的push中,一开始还是调用了array原型上的push,然后,vue会重新解析模版,生成新的虚拟DOM,diff算法,然后更新页面。

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

数据劫持:对于一个数据,把数据及数据内层所有子变量都赋予一个get和set,把这种动作行为叫做劫持。当有人修改或读取数值时,都会被劫持,进入set或get。

http://www.lryc.cn/news/599851.html

相关文章:

  • 局域网 IP地址
  • Linux tcpdump 抓取udp 报文
  • 开源语音TTS与ASR大模型选型指南(2025最新版)(疯聊AI提供)
  • 动态规划:从入门到精通
  • 中国开源Qwen3 Coder与Kimi K2哪个最适合编程
  • 电子电子架构 --- 软件项目的开端:裁剪
  • 【IDEA】IDEA中如何通过分支/master提交git?
  • Hadoop 之 Yarn
  • 【软件工程】构建软件合规防护网:双阶段检查机制的实践之道
  • 【AJAX】Promise详解
  • HashMap的线程安全性 vs ConcurrentHashMap
  • 机器学习中knn的详细知识点
  • 基于springboot的候鸟监测管理系统
  • 100条常用SQL语句大全
  • 用毫秒级视频回传打造稳定操控闭环之远程平衡控制系统技术实践
  • LE AUDIO CIS/BIS音频传输时延计算方法
  • 【神经网络概述】从感知机到深度神经网络(CNN RNN)
  • 博客多级评论展示功能实现
  • Class18卷积层的填充和步幅
  • 仙人掌cacti中的RCE案例
  • 【Python】一些PEP提案(四):scandir、类型约束,异步asyncawait
  • win11 使用adb 获取安卓系统日志
  • 黑马点评01 - 项目介绍 短信登录
  • RAG、Function Call、MCP技术笔记
  • HTML+CSS+JS快速入门
  • Jenkins中HTML文件显示样式问题解决方案
  • uniapp使用css实现进度条带动画过渡效果
  • Elasticsearch-ik分析器
  • 轮盘赌算法
  • C语言————原码 补码 反码 (试图讲清楚版)