我不知道的Vue - watch

作者: 朱珠霞 | 来源:发表于2018-10-31 14:09 被阅读69次

    前置知识

    MDN|object.defineProperty()

    两种对象属性描述

    对象里的属性可以分为两种形式,一种是key-value形式的数据描述符,而另外一种就是由getter-setter函数对描述的存取描述符。

    Vue实现响应式效果

    在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)
            }
        }
    }
    
    • 直接赋值一个新的对象给该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 无法监测对象的变动

    从一个项目说起:点我查看代码

    探索原因

    在这里,dataList是一个数组,通过computed 可以轻易地根据数据响应来实时更新一些页面需要显示的数据。

    但,如果使用watch来监听dataList可以发现他的前后value都是一样的,这样的话,监听不就没有意义了?

    查看官方文档,也有关于这方面的描述:


    文档上只是说了结果,并没有说为什么。于是我谷歌了一下,觉得下面这篇讲的比较有道理

    记一次思否问答的问题思考:Vue为什么不能检测数组变动

    看完以后,发现 读源码 其实挺有意思的,可惜自己还是太菜了,等以后有机会 可以尝试一下。

    过细的原因就不详说,这里就根据自己理解总结一下:

    尤大在设计这个功能的时候,出于某种原因(有说是JavaScript的限制,有说是性能问题),并没有将对象/数组的每个属性都过滤成getter/setter。如果没有深入过滤对象的每个属性,那么只能监听到对象的变化,而JavaScript里对象的赋值是引用赋值,虽然属性变化了,但是它引用的地址却一直没有变化,这样的话,当对象的属性值改变了,Vue虽然知道它改变了,但也只能循着引用地址去获得对象,可此时对象的属性的值已经改变了,因此Vue并不能得到变异之前的值。

    (可能理解有出入,如果有错,烦请指正)

    解决方法

    既然 watch无法在变异对象或数组时监听新旧值,那么我们可以先使用JSON.parse()来浅复制一遍data对象,然后在复制的对象上修改,完了重新赋值给该data对象,这样变化前后两个对象是完全不一样的,因为它们的引用地址完全不一样(这样改变data的方式有点像react,当然 原理是不同的 ..),Vue可以循着两个引用地址获得新旧两个value,从而完美实现 对象变动的watch监听。

    解决方法

    相关文章

      网友评论

        本文标题:我不知道的Vue - watch

        本文链接:https://www.haomeiwen.com/subject/nbwotqtx.html