js的array是引用数据类型,以地址形式赋值给变量,所以当使用push等方式对array进行修改时,地址不变,所以不会触发set方法,所以不能向数组的依赖进行更新。
所以通过一个伪造的array原型作为要被监听的array的原型,其中当被监听的array的push等方法被调用时,实际被调用的是伪造array原型的mutator方法,mutator方法中在使用原型方法的同时加上一些附加操作,向array的依赖发送通知。array的依赖在watcher访问时,将watcher添加。想要向通知发送依赖就要找到依赖列表,mutator函数由于是被array调用的,所以mutator的this是array,所以可以通过访问array的属性得到dep,所以要将dep作为属性加到array之上。mutator不能直接访问到dep,所以有了上面的将dep作为array的属性。
那么为什么要把dep放到observer下面呢,为什么不能将defineReactive中的dep作为array的ob属性呢?为了防备observer中直接传入一个array嘛。(经过下面的分析好像observer一开始就不应该传入数组,所以可能是多种实现方式嘛。理论上如果将一个dep作为array的属性保存下来的话,那么这个array在调用mutator的时候,mutator总能访问到这个array,从而访问到依赖列表并进行通知,所以此时重要的是array将dep作为属性保存下来,这个属性在哪里跟mutator在哪里无关,因为mutator访问的总是array,而array要作为属性保存下来的dep只要是array能访问到就行了,(也要让getter访问到,不过都保存属性了,所以getter肯定能访问到,但是definereactive不是通过属性的方式得到的dep,而是通过保存observer实例的方式访问其dep的方式进行的访问)而不管是在observer构造函数里面还是defineReactive函数里面的dep,array都可以访问到,所以是不是不管保存哪里的dep,只要值能够保存下来,那么这个dep就能在gettter里面进行依赖的填充,就能在调用mutator的时候被mutator得到并进行更新通知notify)
observer构造函数和defineReactive函数应该是会先调用observer,如果给传入observer传入一个array,则会将array的原型篡改,并不会调用walk(P24),所以不能侦听到数组自身的变化,但是可以侦听到数组元素的变化(P27);同时,如果传入的是一个对象的话,那么这个对象也不能被侦听,被侦听的是对象的属性。所以可以给observer传递值,对象和数组,传数组的时候不能给数组设定getter和setter也是正常的,就如同传递一个对象也不能监听到这个对象一样,属于正常现象。
当真的只传入一个array到observer的时候,只会把array的原型篡改,然后当array使用push的方式更新的时候,会通过array的ob属性得到dep从而notify依赖,然而这个时候从哪里收集依赖呢?所以不能传递一个数组,传递数组之后,push数组则mutator就会调用数组的ob的dep,但是由于没有setter,所以无法收集依赖,依赖要用到setter来收集。所以Observer里不能传入array,一般情况下Observer是用来监视data对象或函数里面的内容的,所以默认传进来一个对象的话,对象里面有值、对象、数组,首先传进来data对象,由于这个是对象不是数组,所以不会出现只篡改原型而进入不到walk而不无法添加setter和getter的局面,之后对于值、对象、数组,observer进行不同的处理,值直接通过,进行getter、setter设置,对象和则会再次传入observer进行分解,数组则会会再次传入observer进行原型的篡改以及ob属性的添加。那么如果不再这里添加ob属性,而是在defineReacive函数里面将dep属性传给数组应该也有同样的效果。在defineReactive里面加入define(value,’dep‘,dep),则数组有了dep属性,当push数组的时候,mutator的this是数组,mutator通过this.dep可以直接获取dep,从而通知各watcher。
- Post title:vue.js数组使用push等方法时的监听
- Post author:Willem Zhang
- Create time:2021-11-27 14:50:29
- Post link:https://ataraxia.top/2021/11/27/vue-js数组使用push等方法时的监听/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.