深入浅出Vue.js读后总结-第三章-Array的变化侦测

227 阅读2分钟

一、为什么Array的变化侦测和Object的不同?

Object的变化侦测是通过getter收集依赖,setter通知依赖实现的,但是数组的话,一般对于数组的操作都是通过一些原型方法,比如说push,pop,这样的话就不会触发getter和setter了。

二、那怎么追踪变化呢?

ES6之前,js并没有提供可以拦截原型方法的能力,所以vue2中使用自定义的原型方法去覆盖原生的原型方法。 也就是用一个拦截器去覆盖Array.prototype。

三、拦截器

(1)拦截器的构建

拦截器就是一个和Array.prototype一样的Object,只不过里面可以改变数组内容的方法都经过了我们的处理。

其中七个函数能改变数组自身内容,push,pop,shift,unshift,sort,reverse。

对于构建一个拦截器,vue2源码中的处理是:先以Array.prototype原型创建一个arrayMethods空对象,循环遍历这七种方法,然后缓存原始方法,然后用defineProperty在arrayMethods中添加当前方法在defineProperty的value属性中写个自定义的原型方法实现拦截(就可以在其中通知依赖)

(2)拦截器的覆盖

有了拦截器,想生效就必须去覆盖Array.prototype。

所以在vue2源码中,就是在Observe类里面判断,传进来的value是不是数组,如果是对象的话,就用this.walk()设置为响应式,如果是数组的话,就要进一步判断该浏览器环境__proto__是否可用,如果可以使用__proto__,就直接value._proto_ = arrayMethods,如果不可以使用__proto__,则将arrayMethods的方法用defineProperty放在数组上。

四、收集和通知依赖

Array在getter中收集依赖,在拦截器中触发依赖

(1)依赖的位置

Array的依赖列表就放在Observe类里面,因为这样getter能访问到,拦截器也能访问到。(在源码中,Observe直接里面直接就有this.dep = new Dep())

(2)收集和通知依赖

在getter中有个childOb.dep.depend()用来收集数组的依赖,childOb为数组的Observe实例,如果childOb存在,就调用childOb.dep.depend()收集依赖。

然后前面也说过,就是拦截器中自定义的原型方法中用来收集依赖,所以就在执行方法的同时,调用ob.dep.notify()就能通知依赖了(这里的ob和ChildOb其实意思是一样的,都是Observe实例,也能判断数据是否为响应式)

五、Array变化侦测的问题

因为vue2是对原型方法重写然后覆盖进行拦截的,所以有些变化是无法侦测到的:

比如:

this.list[0] = 2;

this.list.length = 0;

如果是用proxy就可以解决这个问题,vue3就解决了

关于Array的变化侦测就总结这么多,如果有什么错误和补充的欢迎各位前辈指出,如果对你有一点帮助的话点个赞再走吧哈哈哈,谢谢!