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的时候要使用get
、set
关键字,示例如下:
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
如有涉及侵权,立即删除。