纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

Vue监听数据的原理 Vue监听数据的原理详解

黑猫几绛   2021-10-09 我要评论
想了解Vue监听数据的原理详解的相关内容吗黑猫几绛在本文为您仔细讲解Vue监听数据的原理的相关知识和一些Code实例欢迎阅读和指正我们先划重点:Vue数据监听,数据监听原理下面大家一起来学习吧

一、引入

首先画一个简单的图

我们在写Vue的时候总会和数据打交道将我们的目标数据写在data中然后在template的差值表达式中通过{{xxx}}的格式可以响应式的渲染数据当data中的数据改变时这里橙色的线就会引起差值表达式的变化那么问题来了我们如何监测到data中数据的改变呢?这里就涉及到了Vue监测数据的问题

二、监测对象  

2.1 为什么需要监测对象

 首先写出需要用到的静态页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>更新数据</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <ul>
            <li v-for="p in persons" :key="p.id">
                {{p.name}} - {{p.age}} = {{p.sex}}
            </li>
        </ul>
    </div>
 
    <script type="text/javascript">
        Vue.config.productionTip = false
        const vm = new Vue({
            el: '#root',
            data:{
                persons: [
                    {id:'001',name:'黑猫几绛',age: 20, sex: '未知'},
                    {id:'002',name:'白猫几绛',age: 23, sex: '男'},
                    {id:'003',name:'阿猫几绛',age: 18, sex: '女'}
                ]
            }
        })
    </script>
</body>
</html>

现在我们假设有一个需求在页面上添加一个按钮通过这个按钮可以修改id为002的数据

<button @click="change">点击修改白猫几绛</button>
 
  methods:{
      change(){
          this.persons[1].name = '白马'
          this.persons[1].age = 17
          this.persons[1].sex = '未知'
      }
  }

逐个属性的修改可以改变id为002的数据但是这样是否有些繁琐我们是否可以将这么长的一段修改改为一个对象?

 this.persons[1] = {id:'002',name:'白马',age: 17, sex: '未知'}

此时在浏览器当中点击按钮后页面并没有任何反应然而出乎意料的是在vm实例对象中persons[1]的数据确确实实得到了更改!

为了解决这个问题我们可以先看看另外一个例子然后通过在浏览器里面输出来查看具体执行过程在理解如何监测后再回来看这个问题

 2.2数据代理

要想理解数据监测还需要理解一个前置概念:数据代理

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>监测对象</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root"></div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        const vm = new Vue({
            el: '#root',
            data:{
               name: '黑猫几绛',
               age: 20
            }
        })
    </script>
</body>
</html>

在以前的时候我们如果想访问data里的数据可以直接通过实例对象如vm.namevm.age拿到具体的数值其实这是做了数据代理后的简化写法

Vue将data进行了一次加工将data中的数据放在了_data中实例对象通过._data的方式可以拿到加工后的数据只不过属性的值不再直接给出而是通过get方法来获取有点类似于面向对象语言如JAVA中获取类内私有变量时使用的getter方法这里还有一个set方法当data里面数据值改变的时候就会调用该set方法导致重新解析模板然后生成新的虚拟DOM进行新旧DOM对比最后更新页面

在这里其实涉及到了源码方面的问题我们在本文中不考虑源码不然太复杂了简化来说其实在Vue内部有这样的一种方法:

vm._data = data = new Observer(data)

在Observer函数中先通过Object.keys获取data的所有属性形成一个数组然后通过遍历该数组使用Object.defineProperty实现数据劫持最后将计算的最终结果交给_data

2.3 对象监测相关API之Vue.set

还是先上静态页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>更新数据</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
       <h2>学校名称:{{name}}</h2>
       <h2>学校地址:{{address}}</h2>
       <hr/>
       <h2>学生姓名:{{student.name}}</h2>
       <h2>学生性别:{{student.sex}}</h2>
       <h2>学生年龄:{{student.age}}</h2>
       <h1>朋友</h1>
       <ul>
           <li v-for="(f,index) in student.friends" :key="index">
               {{f.name}} - {{f.age}}
           </li>
       </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        const vm = new Vue({
            el: '#root',
            data:{
                name:'MIT',
                address:'UUU',
                student:{
                    name: 'tom',
                    age: 20,
                    friends: [
                        {name: 'jack', age: 21},
                        {name: 'mary', age: 20}
                    ]
                },
            }
        })
    </script>
</body>
</html>

也许你会注意到template中出现的sex其实并不存在于data中当输出一个对象中不存在的属性值时会输出undefined但是Vue经过处理后不会显示undefined值也就是说此时页面上学生性别后面是空值控制台也不会出现报错现在提出一个需求我们如果需要为学生添加一个新的性别属性要求是不能改变data中的现有数据即sex不能直接放入data中

根据之前介绍的_data我们可以尝试通过_data直接绑定上sex属性然后发现并没有渲染到页面上其实这里和2.1中的问题有点类似了下面进行分析

我们先打印一下vm的_data看看由下图可以清楚的看到在student对象中成功的添加上了sex属性名和名为未知的属性值但是继续往下看在下面的众多get与set方法中并没有出现针对sex的方法这也就代表着这个后添加的属性并没有成为响应式数据

为了解决这个问题我们可以使用Vue提供的API即Vue.set()方法来让后添加的数据也成为响应式数据该方法第一个参数target表示要往谁身上添加数据第二个参数key表示要添加的属性名是什么第三个参数表示属性值

此时我们可以通过Vue.set(vm._data.student,'sex','未知')不仅可以改变_data中的属性还可以受到监测改变页面中的数据还有一个同样效果的api即vm.$set用法和Vue.set()相同参数也是相同vm.$set(vm._data.student,'sex','未知')用这两个方法添加上去的数据即可成为响应式数据

 

在实现功能后我们可以考虑一下优化简写由于数据代理vm.student === vm._data.student所以可以简写为Vue.set(vm.student,'sex','未知)

接下来在实际编码中来验证浏览器中的想法假设通过点击一个按钮来添加sex属性这里就不再过多描述了直接贴代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>更新数据</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
       <button @click="addSex">点击按钮新增性别</button>
       <h2>学校名称:{{name}}</h2>
       <h2>学校地址:{{address}}</h2>
       <hr/>
       <h2>学生姓名:{{student.name}}</h2>
       <h2>学生性别:{{student.sex}}</h2>
       <h2>学生年龄:{{student.age}}</h2>
       <h1>朋友</h1>
       <ul>
           <li v-for="(f,index) in student.friends" :key="index">
               {{f.name}} - {{f.age}}
           </li>
       </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        const vm = new Vue({
            el: '#root',
            data:{
                name:'MIT',
                address:'UUU',
                student:{
                    name: 'tom',
                    age: 20,
                    friends: [
                        {name: 'jack', age: 21},
                        {name: 'mary', age: 20}
                    ]
                },
            },
            methods:{
                addSex(){
                    // 在methoods中this的指向就是vm实例对象
                    // 这里还可以写成this.$set(this.student, 'sex', '男')
                    Vue.set(this.student, 'sex', '男')
                }
            }
        })
    </script>
</body>
</html>

但是使用这样的方法有一定的局限性在添加成功的时候是设置给了data中的student对象那么假如直接设置给data对象呢?比如假设需要在这里添加一个新的属性建校日期time如果仍然采用Vue.set方法的话就会报错

由此可见该方法只能用于向响应式对象(如这里的data.student)上添加新property且触发视图更新因为Vue无法探测普通的新增property

2.4 为对象赋多个新值

有时候有你可能需要为已有对象赋值多个新 property比如使用Object.assign()或者是_.extend()但是这样添加到对象上的新 property 不会触发更新因此在这种情况下你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象

比如我想向student新增以前并不存在的身高和体重属性我可以这样做:

const add = {'height': 180, "weight": 150}
this.student = Object.assign({},this.student, add)

将原来的对象和新增的对象进行合并后赋值给一个空的新对象然后将该对象赋值给this.student最终数据可以响应式地添加到student身上

三、监测数组

依旧是先上静态页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>更新数据</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
       <button @click="addSex">点击按钮新增性别</button>
       <h2>学校名称:{{name}}</h2>
       <h2>学校地址:{{address}}</h2>
       <hr/>
       <h2>学生姓名:{{student.name}}</h2>
       <h2>学生性别:{{student.sex}}</h2>
       <h2>学生年龄:{{student.age}}</h2>
       <h1>朋友</h1>
       <ul>
           <li v-for="(f,index) in student.friends" :key="index">
               {{f.name}} - {{f.age}}
           </li>
       </ul>
       <h1>爱好</h1>
       <ul>
           <li v-for="(h,index) in student.hobby" :key="index">
               {{h}}
           </li>
       </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        const vm = new Vue({
            el: '#root',
            data:{
                name:'MIT',
                address:'UUU',
                student:{
                    name: 'tom',
                    age: 20,
                    friends: [
                        {name: 'jack', age: 21},
                        {name: 'mary', age: 20}
                    ],
                    hobby:['吃饭','睡觉','打豆豆']
                },
            },
            methods:{
                addSex(){
                    this.$set(this.student, 'sex', '男')
                }
            }
        })
    </script>
</body>
</html>

在控制台中打印vm._data后我们可以清晰的看到对于student中的hobby数组数组整体来说有get和set是响应式的但是数组里面的元素并非响应式而是简单的挂在了数组中这也就是为什么在2.1中当我们尝试对数组索引下标进行重新赋值时页面上并没有响应如果将hobby数组改为对象再打印一下就会发现对象中的属性是响应式的由此我们可以粗略猜测在Vue中如果想要通过索引修改数组中的数值就需要使用某些特殊方法

方法从何而来?现在我们可以看看Vue文档里面是怎么说的在文档列表渲染->数组更新检测->变更方法的页面中有这样一段话:

        Vue 将被侦听的数组的变更方法进行了包裹所以它们也将会触发视图更新这些被包裹过的方法包括:

push()pop()shift()unshift()splice()sort()reverse()

要想修改数组里面的数据我们不妨试试以上的七种方法看到这些方法也许你会想到Array原型链上的方法然而在这里并不完全正确Vue对这些方法进行了一次包裹封装Vue在解析到这些方法的时候第一步仍然是正常的调用Array原型链中的方法第二步则是重新解析模板这也就是为什么使用这些方法可以触发视图更新因为重新解析模板后会生成新的虚拟DOM进行新旧DOM对比最后更新页面通过vm._data.student.hobby.push === Array.prototype.push这个判断为false可以印证这个想法

比如在这里我们如果想要修改hobby当中的打豆豆为学习应该怎么做?

在接触这些知识点之前我会使用下面这段代码紧接而来的就是一个错误的渲染在改变数据后页面中并没有更新

 this.student.hobby[2] = '学习'

接下来我们试试两种方法首先试试刚刚介绍的数组操作方法

this.student.hobby.splice(2,3,'学习')

接下来试试以前介绍过的Vue.set方法

这里补充一下在2.3中我们介绍利用该方法修改对象里面的数值时方法的第二个参数是属性名;而在数组中使用该方法的时候第二个参数应该为数组中元素的下标

this.$set(this.student.hobby,2,'学习')

这两个方法都可以成功修改hobby中的数据并且成功更新到页面中

总的来说不可以通过数组的索引值进行赋值但是可以改变数组的本身指向在之前的图片中我们已经看到hobby数组本身是响应式的是可以通过get和set进行监控的比如在业务逻辑中使用了filter对数组进行过滤然后赋值给在data中已经存在的数组这里我们可以再看看文档中替换数组这一节的介绍如filter、concat、slice这些方法并不会变更原始数组而总是返回一个新数组当使用变更方法时可以用新数组替换旧数组

总结

本篇文章就到这里了希望能够给你带来帮助也希望您能够多多关注的更多内容!


相关文章

猜您喜欢

  • Java intern方法 Java字符串的intern方法有何奥妙之处

    想了解Java字符串的intern方法有何奥妙之处的相关内容吗吾日三省贾斯汀在本文为您仔细讲解Java intern方法的相关知识和一些Code实例欢迎阅读和指正我们先划重点:Java,intern方法,Java,String,intern()下面大家一起来学习吧..
  • React组件的生命周期 React组件的生命周期详细描述

    想了解React组件的生命周期详细描述的相关内容吗Darlingmi在本文为您仔细讲解React组件的生命周期的相关知识和一些Code实例欢迎阅读和指正我们先划重点:React组件,React组件生命周期下面大家一起来学习吧..

网友评论

Copyright 2020 www.gamerfx.net 【游戏天空】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式