前置知识
对象里的属性可以分为两种形式,一种是key-value形式的数据描述符,而另外一种就是由getter-setter函数对描述的存取描述符。
在Vue里,就是通过遍历data,使用object.defineProperty()来将数据转换成data的存取描述符,然后通过getter-setter来监测数据的变化,从而达到响应式的效果。
那么问题来了,基于这样的设计限制,我们在改变data的时候,自然会有一些限制:当data为对象时,我们不能动态地添加对象属性。这是由于 动态新增的属性,默认是数据描述符,无法通过getter/setter来监测属性的变化。
解决方法:在初始化data时,给该属性一个初始化的值,可以用null、空字符串或其他值。
使用vue的set API
# data
{
family:{
father:{
name:'Jack'
},
mother:{
name:'Rose',
pregnant:false
}
}
}
# methods
{
born(){
if(mother.pregnant){
setTimeout(()=>{
this.$set(this.family,'son',{name:'Jason'}) // vm.$set(target,key,value) },100000)
}
}
}
3. 直接赋值一个新的对象给该data,还是上面那个例子
# data
{
family:{
father:{
name:'Jack'
},
mother:{
name:'Rose',
pregnant:false
}
}
}
# methods
{
born(){
let copyFamily = JSON.parse(JSON.stringfy(this.family))
if(mother.pregnant){
setTimeout(()=>{
copyFamily[son] = {
name:'Jason',
age:0
}
this.family = copyFamily
},100000)
}
}
}
至此,我以为我已经掌握了 Vue 响应式,直到我又遇到了一个新的坑 :point_down:
Vue watch 无法监测对象的变动
从一个项目说起:Vue - JSFiddlejsfiddle.net
探索原因
在这里,dataList是一个数组,通过computed 可以轻易地根据数据响应来实时更新一些页面需要显示的数据。
但,如果使用watch来监听dataList,可以发现他的前后value都是一样的,这样的话,不就不可以监听了?
查看官方文档,也有关于这方面的描述:
image.png
文档上只是说了结果,并没有说为什么。于是我谷歌了一下,觉得下面这篇讲的比较有道理记一次思否问答的问题思考:Vue为什么不能检测数组变动segmentfault.com
看完以后,发现 读源码 应该挺有意思的,等以后有时间了 可以尝试一下。
过细的原因就不详说,这里就总结一下:
尤大在设计这个功能的时候,出于某种原因(有说是JavaScript的限制,有说是性能问题),并没有将对象/数组的每个属性都过滤成getter/setter。如果没有深入过滤对象的每个属性,那么只能监听到对象的变化,而JavaScript里对象的赋值是引用赋值,虽然属性变化了,但是它引用的地址却一直没有变化,这样的话,当对象的属性值改变了,Vue并不能得到变异之前的值。
(可能理解有出入,如果有错,烦请指正)
解决方法
既然watch 无法在变异对象或数组时监听新旧值,那么我们可以先使用JSON.parse来浅复制一遍data对象,然后在复制的对象上修改,完了重新赋值给该data对象,就可以完美实现 对象变化的watch监听。Vue - JSFiddlejsfiddle.net
我的其他平台| 简书 : (关于Vue响应式的一些坑)