Vue数据代理与数据监测

一、数据代理

1.什么是数据代理?

数据代理:通过一个对象代理对另一个对象中属性的操作

2.Vue数据代理

通过vm对象来代理配置对象data中所有属性的操作。
(如果想访问data里的数据,可以直接通过实例对象,如vm.name,vm.age拿到具体的数值,之所以能这样做,就是因为Vue实现了数据代理)

3.为什么要用数据代理

更方便的读取和修改data中的属性

4.为什么要先将数据收集到_data中,再代理出去

为了更高效地实现数据监测

5.实现原理

①Vue将配置对象data进行了一次加工,然后将加工后的数据收集到vm._data中,实例对象通过vm._data的方式可以拿到加工后的data。但是属性的值不再直接给出,而是通过响应式getter来获取,当data中数据改变的时候,就会调用响应式setter,导致重新解析模板,然后生成新的虚拟DOM进行新旧DOM对比,最后更新页面。
在这里插入图片描述

②然后通过Object.defineProperty()让vm上拥有vm._data中的所有属性,即配置对象data中的所有属性,从而实现了数据代理。

模拟数据绑定到vm._data的过程:

   <script type="text/javascript">
        let data={
            name:"gyc",
            age:18
        }

        // 创建一个Observer实例对象,用于监视data中所有属性的变化
        const obs=new Observer(data)
        console.log(obs)

        // 准备一个vm实例对象
        let vm={}
        //将obs赋值给vm._data,从而vm._data可以监视data中属性的变化
        vm._data=data=obs

        function Observer(obj){
            //获取监视对象obj的所有属性
            const keyArr=Object.keys(obj)
            // 遍历属性,将obj的所有属性定义给Obeserver实例对象
            keyArr.forEach((key)=>{
                Object.defineProperty(this,key,{
                    get(){
                        return obj[key]
                    },
                    set(val){
                        obj[key]=val
                    }
                })
            })
        }
    </script>

在这里插入图片描述
在这里插入图片描述

注意:_data里面不是数据代理,而是做了一个数据劫持。Vue去监测_data中的数据变化,从而达到界面也跟着变化。
添加到vm上的属性才是数据代理

二、数据监测

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">
       <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:'JLU',
                address:'jilin',
                student:{
                    name: 'gyc',
                    age: 20,
                    friends: [
                        {name: 'jack', age: 21},
                        {name: 'mary', age: 20}
                    ]
                },
            }
        })
    </script>
</body>
</html>

在这里插入图片描述
需要注意的是,模板中的{{sex}}并没有出现在data中。当输出一个对象中不存在的属性值时,会输出undifined,但经过Vue处理后,不会将undifined显示到页面上,而是什么都不显示,控制台也不报错。

现在的需求是,添加一个按钮,点击按钮,为学生添加性别属性。要求不能改变data中现有的属性,即sex不能直接添加到data中。

前面提到过_data,考虑将sex添加到_data中,浅试一下:
在这里插入图片描述
页面效果:
在这里插入图片描述
可以发现,添加的sex属性并没有渲染到页面上
查看vm._data:
在这里插入图片描述
可以看到通过这种方式添加属性,_data中添加了sex,但是没有生成相应的setter和getter,这说明后添加的属性不是响应式属性,无法渲染到页面上。

2.Vue.set监测对象

为了解决上面的问题,为后添加的属性设置响应式,需要使用Vue提供的API:
​ Vue.set(target,propertyName/index,value)
​ vm.$ser(target,propertyName/index,value)

通过Vue.set()试一下是否可以实现功能:
在这里插入图片描述
页面效果:
在这里插入图片描述
发现通过这种方式可以将新添加的属性渲染到页面上。
在实现功能后我们可以考虑一下优化简写,由于数据代理,vm.student === vm._data.student,所以可以简写为Vue.set(vm.student,‘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">
       <h2>学校名称:{{name}}</h2>
       <h2>学校地址:{{address}}</h2>
       <hr/>
       <h2>学生姓名:{{student.name}}</h2>
       <button @click="addSex">点击添加性别</button>
       <h2 v-show="student.sex">学生性别:{{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:'JLU',
                address:'jilin',
                student:{
                    name: 'gyc',
                    age: 20,
                    friends: [
                        {name: 'tom', age: 19},
                        {name: 'richard', age: 20}
                    ]
                },
            },
            methods:{
                addSex(){
                    Vue.set(this.student,'sex','女')
                }
            }
        })
    </script>
</body>
</html>

在这里插入图片描述
但是使用这样的方法有一定的局限性,上面的例子是给data中的student对象添加新的属性,假如直接给data对象添加属性呢?
比如假设需要在这里添加一个新的属性建校事件time,如果仍然采用Vue.set方法的话,就会报错。
在这里插入图片描述
注意:Vue.set只适用于给data中的对象添加新的属性(例如:data.student),而不能直接给data添加新的属性。

3.监测数组

先给出静态页面:

<!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:'JLU',
                address:'jilin',
                student:{
                    name: 'gyc',
                    age: 20,
                    friends: [
                        {name: 'tom', age: 19},
                        {name: 'richard', age: 20}
                    ],
                    hobby:['吃饭','睡觉','打豆豆']
                },
            },

            methods:{
                addSex(){
                    this.$set(this.student, 'sex', '男')
                }
            }
        })
    </script>
</body>
</html>

其中student.hobby是数组
此时页面效果为:
在这里插入图片描述
在控制台中打印vm._data后我们可以清晰的看到,对于student中的hobby数组,数组整体来说有get和set,是响应式的。但是数组里面的元素并非响应式,而是简单的挂在了数组中。
在这里插入图片描述
现提出一个需求,将hobby数组中的第一项 吃饭 改成 学习。
如果通过索引来改变数组中的值,将hobby[0]改为学习:
在这里插入图片描述
vm._data中已经修改了,但是没有渲染到页面上,页面没反应:
在这里插入图片描述
在Vue中,如果想要通过索引修改数组中的数值,就需要使用某些特殊方法

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

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

这些方法都会引起数组本身的变化,称为变更方法

因此,想要实现上面提出的需求,可以通过以下两种方法实现:
①通过splice()方法

vm.student.hobby.splice(0,1,'学习')

②通过Vue.set()

Vue.set(vm.student.hobby,0,'学习')

注意这里Vue.set()的第二个参数是数组中元素的下标。

对于一些数组的非变更方法,例如filter()、concat() 和 slice(),并不会引起数组本身的变化,若想要调用这些方法实现响应式,可以用新数组替换旧数组。例如:

 this.student.hobby=this.student.hobby.filter((item)=>{
                        return item!='吃饭'
                        })

4.Vue监测总结

1.vue会监视data中所有层次的数据

2.如何监测对象中的数据?

     ​ 通过setter事件监视,且要在new Vue时就传入要监测的数据

          (1).在对象后追加的属性,Vue默认不做响应式处理

          (2).如需给后添加的属性做响应式,需要使用以下API:

           ​ Vue.set(target,propertyName/index,value)

           ​ vm.$ser(target,propertyName/index,value)

3.如何监测数组中的数据?

​    通过包裹数组更新元素的方法实现,本质就是做了两件事:

        (1).调用原生对应的方法对数组进行更新

        (2).重新解析模板,进而更新页面
   
4.在Vue修改数组中的某个元素需要用到以下方法:1.使用这些API:push(),pop(),shift(),unshift(),splice(),sort(),reverse()2.Vue.set()或vm.$set
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值