第9集丨Vue 江湖 —— 监测数据原理

一、修改数据时的一个问题

下面案例中,我们定义了一个按钮,期望是:点击按钮更新persons[0] 的信息。

但实际问题是:Vue监测不到数据的变化。

<div id="root">
    <h2>列表过滤</h2>
    <button @click="change">更新令狐冲信息</button>
    <ul>
        <li v-for="(p,index) in persons" :key="p.id" >
            {{p.name}} - {{p.use}} - {{p.age}}
        </li>
    </ul>
    
</div>
<script>
    const vm = new Vue({
        el:'#root',
        data:{
            persons:[
                {id:'001',name:'令狐冲',use:"独孤九剑",sex:'男',age:20},
                {id:'002',name:'任盈盈',use:"仙女下凡",sex:'女',age:19},
                {id:'003',name:'任我行',use:"吸星大法",sex:'男',age:50}
            ]
        },
        methods: {
            change(){
                // 可行的方式
                // this.persons[0].name = "假的令狐冲";
                // this.persons[0].use = "葵花宝典";
                // this.persons[0].age = 100;

                // Vue没发现你修改了数据,其实已经改了
                this.persons[0] = {id:'001',name:'假的令狐冲',use:"葵花宝典",sex:'男',age:100}
                
            }
        },
    })
</script>

1.1 现象一

  1. 在浏览器中打开页面,在打开vue-devtools
  2. 点击按钮
  3. 观察工具中的数据,竟然没有发生变化!!!但其实数据已经真实改变了
  4. 我们可以通过控制台,输入vm.persons[0] 查看,发现数据确实变了。

在这里插入图片描述

1.2 现象二

  1. 在浏览器中打开页面,然后先点击按钮
  2. 再打开vue-devtools
  3. 查看工具中数据,发现数据改变了!!!

在这里插入图片描述

二、Vue监测数据原理

2.1 模拟一个数据监测

下面案例中,当data的数据发生变化,就会在控制台打印出一句话。

let data = {
    name:'令狐冲',
    menpai:'华山派'
}

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

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

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

2.2 数据劫持

数据劫持:在访问或修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作,如修改返回结果。

Vue 2.x 使用的是 Object.defineProperty(),而 Vue3.x 版本之后改用 Proxy 进行实现

2.3 Vue.set()/vm.$set()

  • 语法:Vue.set( target, propertyName/index, value )

  • 功能:向响应式对象中添加一个 property,并确保这个新property同样是响应式的,且触发视图更新

  • 注意:它必须用于向响应式对象上添加新 property,因为 Vue无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi')

 Vue.set(this.person.hobby, 0, "哈哈哈")
 this.$set(this.person.hobby, 0, "哈哈哈")
 // 从第0个位置,删一个,在插入一个
// this.person.hobby.splice(0,1,"哈哈哈")

2.4 基本原理

Vue监测到data中的数据(所有层次的数据)发生变化,就会调用setter重新解析模板,解析引用的到的数据的值。

2.4.1 如何监测对象中的数据?

  • 通过setter实现监视,且要在new Vue时就传入要监测的数据。

    (1). 对象中后追加的属性,Vue默认不做响应式处理
    (2). 如需给后添加的属性做响应式,请使用如下API:

      Vue.set(target, propertyName/index,value)
      vm.$set(target, propertyName/index,value)
    

2.4.2 如何监测数组中的数据?

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

  • (1). 调用原生对应的方法对数组进行更新。
  • (2). 重新解析模板,进而更新页面。

数组中这些被包裹过的方法包括:

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

2.4.3 修改数组中的某个元素

Vue中,修改数组中的某个元素一定要用如下方法,才能实现响应式:

  • 被包裹过的方法:push()、pop()、shift()、unshift()、splice()、sort(). reverse()
  • 通过Vue.set()vm.$set()

注意Vue.set()vm.$set() 不能给vmvm的根数据对象(vm.data)添加属性。

2.5 案例

2.5.1 需求功能

  • 需求如下截图

在这里插入图片描述

2.5.2 实现

<div id="root">
    <h2>笑傲江湖</h2>
    <hr>
    <div>
        <button @click="person.age++">年龄+1岁</button> 
        <button @click="addSex">添加性别属性,默认值:男</button> 
        <button @click="addFriend">在列表首位添加一个朋友</button>
        <button @click="updateFName">修改第一个朋友的名字为:小三</button>
        <button @click="addHobby">添加一个爱好</button>
        <button @click="updateHobby">修改第一个爱好为: 哈哈哈</button>
        <button @click="removeHobby">过滤爱好中的喝酒</button>
    </div>
    <h3>姓名:{{person.name}}</h3>
    <h3>年龄:{{person.age}}</h3>
    <h3 v-if="person.sex">性别:{{person.sex}}</h3>
    <h3>爱好:</h3>
    <ul>
        <li v-for="(h,index) in person.hobby" ::key="index">
            {{h}}
        </li>
    </ul>

    <h3>朋友:</h3>
    <ul>
        <li v-for="(f,index) in person.friends" ::key="index">
            {{f.name}} - {{f.age}}
        </li>
    </ul>

</div>
<script>
    const vm = new Vue({
        el:'#root',
        data:{
            person:{
                name:'令狐冲',
                age:26,
                hobby:['喝酒','练剑','行侠仗义'],
                friends:[
                    {name:'田伯光',age:30},
                    {name:'任我行',age:50}
                ]
            }
        },
        methods: {
            addSex(){
                // Vue.set(this.person,'sex','男')
                this.$set(this.person,'sex','男')
            },
            addFriend(){
                this.person.friends.unshift({name:'任盈盈',age:'26'})
            },
            updateFName(){
                this.person.friends[0].name = '小三'
            },
            addHobby(){
                this.person.hobby.push('大笑')
            },
            updateHobby(){
                // 从第0个位置,删一个,在插入一个
                // this.person.hobby.splice(0,1,"哈哈哈")
                // Vue.set(this.person.hobby, 0, "哈哈哈")
                this.$set(this.person.hobby, 0, "哈哈哈")
            },
            removeHobby(){
                this.person.hobby = this.person.hobby.filter((h)=>{
                    return h !== "喝酒"
                })
            }
        },
    })
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值