目录
说到Object.defineProperty()这个方法,它是给大家可不要小瞧它,Vue底层很多地方都用到了它。比如:数据劫持、数据代理、计算属性等等。
一、Object.defineProperty()
<script>
let number = 18;
let person = {
name:'王仕丹'
}
Object.defineProperty(person,'age',{
enumerable:true,//控制属性是否可以被枚举,默认为false
writable:true, //控制属性是否可以被修改,默认为false
configurable:true,//控制属性是否可被删除,默认为false
get(){
console.log('有人读取age')
return number
},
set(value){
console.log('有人修改age')
number = value
}
})
</script>
Object.defineProperty()的第一个参数是给谁添加属性,第二个参数是添加什么属性,第三个参数是配置项,里面有enumerable、writable、configurable、getter、setter等。
工作原理就是当有人读取这个属性时,会把返回值作为属性值传出去,当有人修改这个属性时,会把修改的这个新值赋值给这个变量。
二、简单的数据代理小例子
<script>
Vue.config.productionTip = false
let obj = {
x:1
}
let obj2 = {
y:2
}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
</script>
当有人读取obj2.x时,getter会把obj.x返回出去,当有人修改obj2.x时,setter会把新值赋值给obj.x。
三、数据代理
我们就浅说一下vue中的数据代理吧~
举个栗子:
<body>
<div id="root">
<h2>学校:{{school}}</h2>
<h2>地址:{{address}}</h2>
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
school:'阜阳师范大学',
address:'颍州区'
}
})
</script>
</body>
我们可以看到,我们通过vm可以访问到data中的school属性,也可以通过vm修改data中的school属性值,这是为什么呢?
其实它底层原理是Object.defineProperty()这个方法,我们先来看一下数据代理的原理图:
当我们创建vm这个实例对象时,Vue会把data中的数据原封不动的赋值给_data,并且data会有这样一个动作,就是往vm实例对象身上添加data对象中的所有属性,且每一个属性都会有自己的getter和setter。
当有人读取这个属性时,调用getter,getter读取data中的这个属性,并把data中的这个属性的属性值return出去,也就是作为返回值。
get(){
return '我是你想要的data中的属性值'
}
当有人修改这个属性时,调用setter,setter会把你修改的新值在内部做一个赋值操作,也就是把原data中的属性值修改了,data中的数据修改了,当然页面也会重新渲染。
set(value){
school = value
}
四、计算属性(Computed)
emmm……说到计算属性呢……我来想想要怎么来讲……
首先我们先来搞明白什么是计算属性吧~
定义:要用的属性data中不存在,要通过已有属性计算得来。
之前我们也说了,计算属性底层的原理也是借助了Object.defineProperty()这个方法提供的getter和setter,那么它这两个函数在什么时候执行呢?
<body>
<div id="root">
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
全名:<span>{{fullName}}</span>
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data:{
firstName: '张',
lastName: '三'
},
computed:{
fullName:{
get(){
console.log('get被调用了')
return this.firstName +'-'+ this.lastName
}
}
}
})
console.log(vm)
</script>
</body>
我在get函数中输出打印了一下,方便我们观察get函数什么时候被调用了。
可以看到,我们没有任何操作,但是在初始化的时候就已经调用了一次。那如果,我们在页面中用了很多次fullName呢?我们来看看。
<div id="root">
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
全名:<span>{{fullName}}</span>
全名:<span>{{fullName}}</span>
全名:<span>{{fullName}}</span>
</div>
可以看到,不管我们使用多少次fullName这个计算属性,getter只被调用了一次。也不卖兜子了,那是因为我们的coputed中有缓存机制,当我们的计算属性所依赖的属性没有发生变化时,就会继续复用我们之前的数据。 这也大大提高了Vue的效率,这也是computed的一大优势!!!
好了,我们来总结一下get函数在计算属性中,什么时候执行了。
- 初次读取时会执行一次
- 当依赖的数据发生改变时会被再次调用
在我们前端的面试中,经常也会问到这样的问题:与methosd实现相比,computed会有什么优势呢?之前我们也说了,computed内部有缓存机制(复用),效率更高,调试方便。
注意:
- 计算属性最终会出现在vm上,直接读取使用即可
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生变化
五、监视属性(Computed)
我们先来看一个小案例。
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click = 'isHot = !isHot'>点击切换天气</button>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
isHot:true
},
computed:{
info(){
return this.isHot ? '炎热':'凉爽'
}
},
watch:{
isHot:{
// deep:true,
immediate:true,
handler(oldvalue,newvalue){
console.log('isHot被修改了',oldvalue,newvalue)
}
}
}
})
</script>
</body>
这是一个关于切换天气的一个小案例,添加了一个watch的配置项,当我们监视的属性【isHot】发生变化时,就会调用handler函数,函数中有两个参数,分别是变化前的旧值和变化后的新值。其中,有两个属性。【deep:true】,是否开启深度监视?vue是具备深度监视这个功能的,但是需要我们把【deep】从false改为true;还有一个属性【immediate:true】,是否立即执行?当我们的这个监视过程中,如果是一个漫长的过程,可能要等很久,如果把【immediate】从默认的false改为true,它就不会等这个监视结束,会立即执行。
除了在new Vue里这样用对象的形式写之外,我们还可以这样写:
vm.$watch('isHot',(ewvalue,oldvalue)=>{
console.log('isHot被修改了',newvalue,oldvalue)
})
其实这种写法其中的handler函数我用了简写,只要不需要【immediate】和【deep】配置项,我们都可以简写,比如第一种写法:
isHot(newvalue,oldvalue){
console.log('isHot被修改了',newvalue,oldvalue)
}
好了好了,今天就说到这里。拜比~