vue数据代理
Vue实现数据代理的核心----Object.defineProperty();
数据代理
数据代理的定义是:一个对象操作(读\写)另一个对象中的属性和方法。
// 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
let obj = { x: 100 }
let obj2 = { y: 200 }
Object.defineProperty(obj2, 'x', {
get() {
return obj.x
},
set(value) {
obj.x = value
},
})
// 当访问obj2上的x属性时,就会调用get方法读取obj的属性,当我们修改了obj2上x属性时,就会调用set方法,并且传入修改的值value值,将obj的x属性改为value
Vue中的数据代理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./vue.min.js"></script>
</head>
<body>
<!-- 1.Vue中的数据代理:
2.Vue中数据代理的好处:更加方便的操作data中的数据。
3.基本原理:通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
-->
<div id="root">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
let data = {
name: 'xizhutao',
address: '中国',
}
const vm = new Vue({
el: '#root',
data
})
console.log("vm",vm);
</script>
</html>
对于上面的案例,vm实例上有data中的数据,访问方式为:vm.name vm.address
,
但不能通过vm.data.name 或 vm.data.address
进行访。因为实例中的data被编译后在vm中就不是data,而是vm._data;即:vm._data == data ;
vue实例中的数据到通过vm.name进行访问,就是object.defineProperty() 所做的工作。
详细分析一波
打印一下vm实例,看看里面有那些东西:
其中vm.name 以及 vm.address 属性是直接暴露在vue实例上的,这里的实例指的就是vm。借助官网的解释:
使用选项式 API,我们可以用包含多个选项的对象来描述组件的逻辑,例如
data
、methods
和mounted
。选项所定义的属性都会暴露在函数内部的this
上,它会指向当前的组件实例。
是的,这里的API风格就是选项式API。
那么,data中的数据是如何暴露在其对应的实例中的呢?该工作就是由 数据代理的核心:object.defineProperty() 做的
首先,实例中的data会被实例编译为 vm中的 _data,然后object.defineProperty() 把 _data中的数据代理为 vm 下的name 和 address,也就实现了暴露在vm实例上。如图所示:
我们操作的是vm.name 和 vm.address
访问vm.name ,object.defineProperty()就会通过其内置的get方法,把_data中的数据获取到并展示出来。
修改vm.name,object.defineProperty()就会通过其内置的set方法,等同于在_data中修改name;因为_data中的数据发生了改变,即是vm 实例中的 data 发生了改变,所以,页面上也会同步的更新。
object.defineProperty() 详解
Object.defineproperty方法需要传递3个参数;Object.defineproperty(obj, prop, desc )
参数
- 参数1:obj 需要定义属性的当前对象
- 参数2:prop 当前需要定义的属性名
- 参数3:desc 描述符 一般是一个对象
一般通过为对象的属性赋值的情况下,对象的属性可以修改也可以删除,但是通过Object.defineProperty()定义属性,通过描述符的设置可以进行更精准的控制对象属性。
属性
注意:当使用了getter或setter方法,不允许使用writable和value这两个属性(如果使用,会直接报错)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Object.defineproperty方法</title>
</head>
<body>
<script type="text/javascript" >
let number = 18
let person = {
name:'张三',
sex:'男',
}
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true, //控制属性是否可以枚举,默认值是false
// writable:true, //控制属性是否可以被修改,默认值是false
// configurable:true //控制属性是否可以被删除,默认值是false
//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get(){
console.log('有人读取age属性了')
return number
},
//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value){
console.log('有人修改了age属性,且值是',value)
number = value
}
})
// console.log(Object.keys(person))
console.log(person)
</script>
</body>
</html>