Vue的响应式数据和数据代理

Vue的响应式数据

在介绍Vue的数据代理之前,我们先来看一下Vue的响应式数据带来的效果是什么。在Vue中,如果我们在data中放置了数据,并且将其展示到页面中。

例如像下面这样:

<body>
    <div>
        <h1>Vue的数据代理</h1>
    </div>
    <div id="app">
        姓名:{{username}}
        <br>
        性别:{{usersex}}
        <br>
        工资:{{wage}}
    </div>
</body>
<script>
    const vm = new Vue({
        el:"#app",
        name:'App',
        data(){
            return {
                username:'张三',
                usersex:'男',
                wage:1000
            }
        },
    })
</script>

 如果我们对data中的数据进行改动(这里我直接借助Vue开发者工具插件进行修改,实际上methods等方法修改效果也是一样),页面上的数据也会随之改变。

效果如图:

动图
 

从上面我们可以看出,只要data中的数据改变了Vue就能够很智能的去修改页面上的对应的数据,他实际上就是去监听data中数据的每一次改变,只要data中的数据改变了,就去重新渲染页面,这样新的数据也就被展示到了页面中,这样就能够使得页面能够根据数据的改变而改变。

Vue对数据做了什么?

由于Vue对数据做了一些了的特殊的处理,使得普通的数据能够变成响应式的数据,我们可以对比一下数据经过Vue处理的前后数据有什么不同,从而观察一下Vue究竟做了什么处理。

我们创建一个对象,并将其打印出来

const myObj = {
    a:1,
    b:2,
    c:3
}
console.log(myObj);

打印结果如下:

我们将创建的对象数据放在Vue实例的data中,然后将其打印出来

const myObj = {
    a:1,
    b:2,
    c:3
}
const vm = new Vue({
    el:"#app",
    name:'App',
    data(){
        return {
            myObj,
        }
    }
})
console.log(myObj);
打印结果如下:

通过对比我们能够看到同样的数据经过处理之后,对象数据的值变成了(...)并且还多了很多get/set 属性名的函数,这也就是Vue对数据所做的处理。

getter和setter

想要看懂上面的数据被做了哪些处理,我们需要了解一些额外的知识,首先我们要了解什么是getter和setter。

getter和setter我们可以简单理解为是为了防止用户直接操作数据,而提供的两个方法,通过getter我们能够直接获取到数据的值,通过setter我们能够修改数据的值。

const obj = {
    num:123,
    getter(){
        return this.num
    },
    setter(value){
        this.value
    }
}

ES6中的getter和setter

了解了getter和setter是什么之后,我们来看一下在ES6中提供给我们的getter和setter的语法是怎样的。

在ES6中我们要使用getter和setter的时候要使用getset关键字,示例如下:

 1 var obj = {
 2     num: 7,
 3     get getNum() {
 4         return this.num;
 5     },
 6     set setNum(value) {
 7         this.num = value / 2
 8     }
 9 };
10 console.log(obj);
11 12 // getter的使用:能够直接使用obj.getNum调用getNum方法去获取num的值
13 console.log('getter获取num的值为:' + obj.getNum);
14 15 // setter的使用:能够使用obj.setNum设置num的值
16 obj.setNum = 40;
17 console.log('setter设置num的值为:' + obj.getNum);

控制台输出:



我们可以看到,使用了get和set关键字后,obj对象发生了一些变化,变得和我们前面Vue处理过的数据有点相似,同样拥有get和set的函数,并且我们能够看到还新增了getNum这个属性,并且这个属性的值是(...),这是实际上是因为,这个属性的值浏览器是不知道的,他的值是需要去其他地方获取,或者通过一些其他方式产生的。

Object.defineProperty()

介绍了上面的getter和setter之后,想必大家会有一个疑问,在之前的Vue对象中,data中的每一个属性值都有自己的getter和setter,并且data中不会有任何的多余的新增属性。而上面的get和set关键字虽然能够产生getter和setter函数,但是和已有的属性联系却不是那么的紧密。

这个时候就需要介绍一下Object.defineProperty()这个方法了,他能够在指定对象身上新增一个属性,并且能够为其绑定getter和setter方法

示例如下:

let number = 18; // 用来真正存放age的值
let person = {
    name:'张三',
    sex:'男',
}
Object.defineProperty(person,'age',{
    enumerable:true,  // 控制属性是否可以枚举,默认值是false
    // writable:true,  // 控制属性值是否可以被修改,默认值是false
    // configurable:true,  //控制属性是否可以被删除,默认值是false
    get:function(){
        console.log('有人读取了age属性的值');
        return number
    },
    set:function(value){
        console.log('有人修改了age,且值是'+value);
        number = value;
    }
});
console.log(person);

控制台输出:

从输出的内容我们可以看出,Object.defineProperty()方法为我们在person中增加了一个age属性,并且为age属性绑定了getter和setter方法,其实到这个时候我们已经能够发现这个对象数据经过Object.defineProperty()处理之后跟Vue处理后的数据的已经很相似了。

在我们对age属性进行读取或者更改的时候

 

它就会调用对应getter和setter方法,我们可以在此对属性进行监听,随后我们只需要在数据改变的时候对页面进行重新渲染,就能够得到类似于Vue的响应式数据了。

Vue的数据代理

上面我们讲完了数据的响应式,我们回过头继续研究vm实例对象本身,下面这张图是在控制台打印出的vm实例对象,我们不难发现,在它的身上,我们放置在data上的myObj对象数据已经它对应的getter和setter,不只出现了一次。



这是因为Vue做了数据代理,Vue将data中的数据放在了实例自身的_data上,然后将_data上的数据通过Object.defineProperty()方法代理到vm自身上,这样vm就出现了_date上的属性,并且可以通过操作vm身上的属性来操作_data中的的数据,

过程如下图所示:



这样进行数据代理的好处是:

vm能够更方便的操作data中的数据。

实现一个数据代理

// 准备一些数据
let data = {
    name:'张三',
    age:14,
    sex:'男'
}
​
// 代理
let proxy = dataProxy({data:data}) 
// ES6可以简写成dataProxy({data})
function dataProxy({data}){
    const proxy = {}
    for(key in data){
        let temp = data[key]
        // 这里要注意不能直接使用data[key]不然会重复触发get函数
        // 后会导致爆栈
        
        // 这一部分是为了操作原本的data也能够被监听到
        Object.defineProperty(data,key,{
            get(){
                return temp
            },
            set(value){
                temp = value
            }
        })
        
        // 这里是使用proxy对data进行数据代理
        Object.defineProperty(proxy,key,{
            get(){
                return temp
            },
            set(value){
                temp = value
            }
        })
    }
    
    // 返回代理对象
    return proxy
}

 

通过控制台输出我们可以看到,原本的data属性已经全部拥有了getter和setter方法,并且proxy也获取到了data身上的全部属性以及方法

 

我们测试一下proxy能否代理data身上的属性,我们直接修改proxy身上的age属性,这是我们可以发现data上的age属性也相应产生了改变,证明proxy已经成功的完成了对data的数据代理

 

 

本文章转载自https://zhuanlan.zhihu.com/p/539327790

如有涉及侵权,立即删除。

posted @ 2022-09-01 12:29  laya1211  阅读(128)  评论(0)    收藏  举报