Vue响应式官方文档:响应式原理
MDN关于getter和setter的文档:getter、setter、对象初始化
一、getter和setter
getter和setter是ES6的新语法!
getter和setter用于对属性的读写进行监控。
什么是getter:通俗来讲,就是一个以函数形式定义的计算属性,是用于获取一个值的,就是一个不加括号的函数。PS:调用时不需要加括号!
用法:在函数前面加一个get,且调用该函数时省去括号。
//类似这样的写法就叫做getter
let person1 = {
姓: "张",
名: "富贵",
年龄: 8,
get 姓名(){
return this.姓 + this.名
}
}
console.log(person1.姓名 + "今年" + person1.年龄 + "岁。")
//注意调用的时候没有加括号!这个写法就是getter!“姓名”这时候是一个计算属性!调用的时候不加括号!
//打印出“张富贵今年8岁。”
以上代码只能在对象person1中读出姓名。那我要写怎么办?
那就用setter!
//类似这样的写法就叫做setter
let person2 = {
姓: "张",
名: "富贵",
年龄: 8,
get 姓名(){
return this.姓 + this.名
},
set 姓名(newName){
this.姓 = newName[0] //暂时不考虑复姓
this.名 = newName.substring(1)
}
}
person2.姓名 = "庄睿" //这一行触发了set函数
console.log("姓:" + person2.姓 + ",名:" + person2.名)
//打印出“姓:庄,名:睿”
//= newName 触发set函数
setter的特点:必须要接受一个新的值才能触发set函数。上面的person2.姓名="庄睿"触发了set函数,"庄睿"就是新的值。
打印一下person2看看里面的“姓名”到底是个啥:
姓名: (...) 的意思是:“姓名”不是一个真实存在的属性,但是我们可以对“姓名”进行读写,且读写操作是通过下面的get 姓名: f 姓名()和set 姓名: f 姓名(newName)完成的。
二、Object.defineProperty
上面的例子对象person2已经声明完了,那如果我还想在person2上添加别的getter和setter呢?而且我还想在对象外面写怎么办?这个时候可以用 Object.defineProperty 解决!
写法:
Object.defineProperty(对象名 , "虚拟属性名" , {
get(){}
set(新值){}
//注意这两个函数不需要再写一次属性名了,第二个参数已经写了!
//注意:这个自己定义的虚拟属性其实是不存在的,只是能用来读写而已。
})
继续上面的例子,我想get和set一下年龄,且不把get和set写在对象中,那就可以用Object.defineProperty解决:
let person3 = {
姓: "张",
名: "富贵",
年龄: 8,
get 姓名(){
return this.姓 + this.名
},
set 姓名(newName){
this.姓 = newName[0] //暂时不考虑复姓
this.名 = newName.substring(1)
}
}
let _newAge = 0 //定义_newAge,用于盛放虚拟属性的值,一定要定义一个用于存放虚拟属性的值的变量!
Object.defineProperty(person3,"年龄",{
get(){
return _newAge //这里的set不能直接return this.newAge,会死循环。
},
set(newAge){
_newAge = newAge
}
})
person3.姓名 = "严小赖"
person3.年龄 = 18 // =18 触发get和set函数
//注意:这个新定义出来的虚拟属性“年龄”会顶替掉旧的那个!
console.log(person3.姓名 + "今年" + person3.年龄 + "岁。")
//打印出“严小赖今年18岁。”
三、Vue对data做了什么
什么是代理(proxy):
代理是一种设计模式。在Vue中对myData对象的属性进行读写会全权交由另一个对象vm负责。vm就是myData的代理。
在我们const vm = new Vue({data:{myData})的时候:
- Vue会让vm成为myData的代理
- Vue会对myData中的所有属性进行监听
- vm知道属性发生了改变就可以调用render(data)
- UI = render(data)
四、所以什么是数据响应式
Vue通过Object.defineProperty来实现数据响应式。比如我const vm = new Vue({data:{n:0}}),如果在我修改vm.n,那么UI中的n就会响应我。
五、data有bug
由于Vue是通过Object.defineProperty(obj,'n',{})来实现数据响应式的,所以必须要传进一个'n',vm才能代理和监听 obj.n 。Vue没办法监听一开始就不存在的 obj.n !
解决方法:
- 在声明的时候就把将来要用到的key全都声明好,value可以先设置成undefined
- 使用Vue.set(this.obj,'b',b的值)或者this.$set(this.obj,'b',b的值)
Vue.set和this.$set的作用:
- 帮忙新增一个key
- 自动创建代理和监听(如果没被创建过)
- 触发UI更新,但不会立即更新
那如果data里面有数组呢?? 数组就没办法提前声明好所有key了呀,我怎么知道数组后来会添加几个值? 解决方法:变异方法
例子:
new Vue({
data:{
array:["a","b","c"]
},
template:`
<div>
{{array}}
<button @click="setD">setD</button>
</div>
`,
methods:{
setD{
this.array.push("d")
}
}
})