(十四)Vue之监测数据改变的原理

Vue学习目录

上一篇:(十三)Vue之列表渲染

下一篇:(十五)Vue之收集表单数据

先看一个需求:使用列表渲染出一组数据,然后点击按钮更新其中一个信息
在这里插入图片描述

<!--准备好一个容器-->
    <div id="root">
        <!--第一步:收集用户输入的数据-->
        <button @click="updateMei">更新马冬梅的信息</button>
        <ul>
            <li v-for="(person,index) in persons" :key="person.id">{{person.name}}-{{person.age}}-{{person.sex}}</li>
        </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false//阻止 vue 在启动时生成生产提示。
        //第二步:使用监视属性或者计算属性进行过滤
        const vm = new Vue({
            el:'#root',
            data:{
                persons:[
                    {id:'001',name:'马冬梅',age:22,sex:'女'},
                    {id:'002',name:'周冬雨',age:19,sex:'女'},
                    {id:'003',name:'周杰伦',age:21,sex:'男'},
                    {id:'004',name:'温兆伦',age:20,sex:'男'}
                ]
            },
            methods:{
                updateMei(){
                    this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'}
                }
            }
        });
    </script>

效果:无论怎么点按钮,数据都不会改变,打开Vue开发者工具,发现数据改变了,但是页面没有改变,不奏效,vue没有承认你这次修改。
请添加图片描述
请添加图片描述
如果把更新的逻辑改成如下:

       updateMei(){
           this.persons[0].name = '马老师'
           this.persons[0].age = 50
           this.persons[0].sex = '男'           
       }

效果:点击按钮,页面数据改变,Vue开发者工具也改变
请添加图片描述
请添加图片描述

监测数据改变的原理之对象

之前说过vm里面的_data存储了data数据,但可不是直接存储,vue会对data里面的数据做一些加工,我们打开发现里面有setter、getter,之前说过不是数据代理,而是数据劫持,你只要一改data数据,setter就会被调用,而且这个setter是一个响应式的setter,响应式的setter就是这个setter里面调了一个方法,这个方法会引起模板的解析。
我们把这种一旦修改data里的数据,就会被setter拦截到,或者获取数据时,会被getter拦截到,叫做数据劫持
那是怎么实现的呢,我们可以模拟一个数据监测。
数据监测步骤:数据监测也是通过Object.defineProperty进行实现的

  • 1.创建一个监视的实例对象,用于监视数据中属性的变化
  • 2.汇总对象中所有的属性形成一个数组
  • 3.遍历,为其添加getter和setter
  • 4.准备一个实例对象,把数据和监视对象赋给它
<script type="text/javascript">
        let data = {
            name:'一中',
            addr:'广州', 
        }
        //1.创建一个监视的实例对象,用于监视data中属性的变化
        const obs = new Observer(data)

        //4.准备一个vm实例对象
        let vm = {}
        vm._data = data = obs

        function Observer(obj){
            //2.汇总对象中所有的属性形成一个数组
            const keys = Object.keys(obj)
            //3.遍历
            keys.forEach((k)=>{
                Object.defineProperty(this,k,{
                    get(){
                        console.log('get被调用')
                        return obj[k]
                    },
                    set(val){
                        console.log(`${k}被改了,去解析模板,生成虚拟DOM.....`)
                        obj[k] = val
                    }
                })
            })
        }
    </script>

效果:不管访问/修改data的数据,还是访问/修改vm里_data的数据,都能监测得到。
在这里插入图片描述
当然我们模拟的监测也是有局限性的,如果写了一个对象,我们当前程序就废了,因为我们只考虑了一层,vue是用递归实现,它会一直往下找,直到找到不是对象为止。vue做得更好的是哪怕是一个数组对象,也能找到最低层。

vue.set的使用

我们想在data外面给data里面的对象添加属性,如果直接进行添加的话,是没有setter和getter,也就是说,vue不会对这个属性进行监测,只是当作普通的一个属性,也没有响应式,在页面不会进行展示。
在vue可以使用vue.set进行添加属性:
Vue里的set,会自动添加setter和getter,function set (target, key, val);
三个参数:

  • target:要添加的对象
  • key:添加的属性名
  • val:添加的属性值

vm上与Vue里的set一样效果
vm.$set()
注意:Vue.set()vm.\$set() 不能给vm 或 vm的根数据对象 添加属性!!!

<div id="root">
        <h1>学校信息</h1>
        <h2>学校名称:{{name}}</h2>
        <h2>学校名称:{{addr}}</h2>
        <h1>学生信息</h1>
        <button @click="addSex">添加性别,默认是男</button>
        <h2>学生姓名:{{student.name}}</h2>
        <h2 v-if="student.sex">学生性别:{{student.sex}}</h2>
        <h2>学生年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h2>
        <h2>朋友们</h2>
        <ul>
            <li v-for="(friend,index) in student.friends" :key="index">
                {{friend.name}}--{{friend.age}}
            </li>
        </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false//阻止 vue 在启动时生成生产提示。
        const vm = new Vue({
            el:'#root',
            data:{
                name:'一中',
                addr:'广州',
                student:{
                    name:'tom',
                    age:{
                        rAge:40,//真实年龄
                        sAge:29,//对外年龄
                    },
                    friends:[
                        {name:'jack',age:35},
                        {name:'tony',age:34}
                    ]
                }
            },
            methods:{
                addSex(){
                    //直接添加没有响应式
                    //this.student.sex = '男'
                    
                    //Vue.set
                    //Vue.set(this.student,'sex','男')
                    //vm.$set()
                    this.$set(this.student,'sex','男')
                }
            }
        });

效果:点击按钮,添加了属性,在vm身上也有setter和getter进行页面的响应

请添加图片描述
在这里插入图片描述

监测数据改变的原理之数组

上面程序基础上添加一个爱好属性,并进行展示

		<h2>爱好</h2>
        <ul>
            <li v-for="(h,index) in student.hobby" :key="index">
                {{h}}
            </li>
        </ul>
		data:{
                name:'一中',
                addr:'广州',
                student:{
                    name:'tom',
                    age:{
                        rAge:40,//真实年龄
                        sAge:29,//对外年龄
                    },
                    hobby:['抽烟','喝酒','烫头'],
                    friends:[
                        {name:'jack',age:35},
                        {name:'tony',age:34}
                    ]
                }
            },

效果:
在这里插入图片描述
去控制台查看vm中的hobby和根friends,发现没有setter和getter,而且通过最上面的程序我们知道直接改数组的数据,不会导致页面的更新。
在这里插入图片描述
那vue是怎么对数组做监测的呢?
vue是通过这些API得知数组的变更:

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

一旦对数组使用了上面的API,vue就会做响应式处理。
那vue怎么得知你使用上面这些API呢?
vue是对这些API进行了包装(装饰器模式),形成了自己的API,如果你通过数组调用这些方法,实际上是调用vue自己写的API。

我们查看hobby和根friends的Prototype,发现了vue对这些API进行了包装
请添加图片描述

vue对这些方法做了两件事:

  • 1.调用数组原型对象对应的方法。
  • 2.进行页面的响应。

数组中的某个元素还可以使用Vue.set()或vm.$set(),也会导致页面数据更新。

注意:filter()、concat() 和 slice()等等。它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忆亦何为

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值