在讨论数据代理之前,大家先要知道它的底层,也就是
Object.defineProperty(给哪个对象添加属性,‘添加的属性名’,{配置项})
这个翻译过来,也就是给对象定义属性,不要小巧它,它在Vue底层中是一个相当重要的角色,不论是这次要说到的数据代理,还是数据劫持,还是Vue中我们熟知的计算属性(computed)等等,都离不开它
我们用代码演示一下,我们给一个person对象添加属性
<script>
let person = {
name: 'shaka',
sex: '男'
}
Object.defineProperty(person, 'age', {
value: 21
})
console.log(person);
</script>
我们毫无疑问会输出这样一个结果,那么,我们会问,为什么不直接在person里面直接写属性呢,我们先来写一下看看
<script>
let person = {
name: 'shaka',
sex: '男',
age: 21
}
console.log(person);
</script>
我们有没有发现一个细节问题,这两个图中的age 颜色不一样,我们直接写的age属性,和别的属性是一个颜色,然而,我们在用Object.defineProperty添加的适合,age的颜色,和另外两个颜色不一样,如果没意识到的话,大家翻上去好好看一下。
那么,用Object.defineProperty添加属性,颜色不一样,代表着什么呢?
它的意思是:
用Object.defineProperty,添加的属性,是不可枚举的
什么意思呢,简单来说,就是用Object.defineProperty添加的age方法是不可被遍历的
我们来验证一下
1.普通方法添加属性
<body>
<script>
let person = {
name: 'shaka',
sex: '男',
age: 21
}
// Object.defineProperty(person, 'age', {
// value: 21
// })
console.log(Object.keys(person));
console.log(person);
</script>
</body>
<body>
<script>
let person = {
name: 'shaka',
sex: '男',
// age: 21
}
Object.defineProperty(person, 'age', {
value: 21
})
console.log(Object.keys(person));
console.log(person);
</script>
</body>
那么,如果我们想要遍历它怎么办?这时候,我们要借助一个配置项
enumerable : true
我们尝试一下
<body>
<script>
let person = {
name: 'shaka',
sex: '男',
// age: 21
}
Object.defineProperty(person, 'age', {
value: 21,
enumerable: true,//控制属性是否可以枚举,默认值是false
})
console.log(Object.keys(person));
console.log(person);
</script>
</body>
好,这样我们就可以枚举了,但是就是加了这个配置项以后,它还是和正常的属性不同,比如,它是不可修改的
这时候我们引入另一个配置项
writable:true
<body>
<script>
let person = {
name: 'shaka',
sex: '男',
// age: 21
}
Object.defineProperty(person, 'age', {
value: 21,
enumerable: true,//控制属性是否可以枚举,默认值是false
writable: true//控制属性是否可以被修改,默认值是false
})
console.log(Object.keys(person));
console.log(person);
</script>
</body>
这回它就可以被修改了,现在还有一点,就是这个age属性无法被删除
这次我们引入下一个配置项
configurable:true
<body>
<script>
let person = {
name: 'shaka',
sex: '男',
// age: 21
}
Object.defineProperty(person, 'age', {
value: 21,
enumerable: true,
writable: true,
configurable: true//控制属性是否可以被删除,默认值是false
})
console.log(Object.keys(person));
console.log(person);
</script>
</body>
这下,就可以进行删除了,但是Object.defineProperty还有别的配置项,这里我们就说这基本的三种
好,我们开始有新的需求
我们用另一个变量来传这个age的值
传统方式:
<script>
let number = 21
let person = {
name: 'shaka',
sex: '男',
age: number
}
console.log(person);
</script>
</body>
可以正常实现是吧,但是,这样会有一个问题,我们后期修改age的值,怎么办?
我们发现改不了,这样,我们就要求助Object.defineProperty了
<body>
<script>
let number = 21
let person = {
name: 'shaka',
sex: '男'
}
Object.defineProperty(person, 'age', {
//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get() {
return number
}
})
console.log(person);
</script>
</body>
当我们鼠标轻放到age的…的时候会出来这个英文提示
这个 getter的意思就是get这个函数的整体
这样,我们就可以对number进行更改了
需要注意的是,只要读取属性了,就会调用get函数!
好,既然有get了,那么就会有一个跟它相辅相成的方法:set
<body>
<script>
let number = 21
let person = {
name: 'shaka',
sex: '男'
}
Object.defineProperty(person, 'age', {
//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get() {
return number
},
//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value) {
console.log('有人修改了age属性,且值是', value)
number = value
}
})
console.log(person);
</script>
</body>
可以自己输出试一下,这里我就不说了
数据代理就是:通过一个对象代理对另一个对象中属性的操作(读/写)
好,概念我们知道了,我们用一个例子比较直观地看一下
我们有两个对象,obj1,obj2,我们通过obj2来改变obj1里面的值
<body>
<!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)-->
<script type="text/javascript" >
let obj = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
</script>
</body>
ok,这就是数据代理了,也可以说数据代理是做什么的了
我们先写一段简单的代码,相信大家都可以看懂
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>名字:{{name}}</h2>
<h2>地址:{{address}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
name: 'shaka',
address: '辽宁东北'
}
})
console.log(vm);
</script>
我们发现Vue实例上就有了address,name这两个属性,我们思考一件很关键的事情,这两个属性,是怎么加上去的
是不是就是Object.defineProperty,发挥了作用,一旦调用了的话,是不是Object.defineProperty中的get方法开始起作用了,
当我们给属性赋值的时候,是不是就是调用了Object.defineProperty中的set方法呢
思考一下这个问题,跟上面的知识串联一下,就明白了
我们读取name,getter就会工作,就会把data.name读取出去
如果有人去改这个name,setter就会工作,就会把data.name给改掉
说了这么多,这个数据绑定究竟有什么作用呢?
Vue中数据代理的好处:更加方便的操作data中的数据
什么意思呢?就是说,我咋没看出来哪里方便了呢?
那么我再引出Vue中数据代理的原理:
基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
还是不明白?那么我带着大家演示一下,没有这个数据代理,我们会是什么情况
<body>
<!--
1.Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
-->
<!-- 准备好一个容器-->
<div id="root">
<h2>名字:{{_data.name}}</h2>
<h2>地址:{{_data.address}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
name: 'shaka',
address: '辽宁东北'
}
})
console.log(vm);
</script>
为什么没有数据绑定会这样呢?那么我反问大家,为什么你会在这个插值里面直接写name和address呢?
你看我直接调data上的这两个属性有意义吗
也就是说,我们无法直接调用data里面的值
但是Vue实例中给我们定义了一个 _data 的属性
我们可以通过vm._data.name或者vm.name来调用
但是如果我们有数据代理的话: 通过Object.defineProperty()把data对象中所有属性添加到vm上。也就是我们看到的name和address
每次我们调用或者修改的时候,都会通过Object.defineProperty中的getter和setter
所以我们就在插值里直接写name和address就行了,所以,Vue中数据代理的好处:
更加方便的操作data中的数据