目录
第3章 Array的变化侦测
Object的侦测方法与Array的不同,Object是通过
getter/setter
的实现方式,而Array是通过原型上的方法来改变数组的内容。
一、如何追踪变化
Obejct的变化是靠
setter
来追踪的,只要一个数据发生了变化,一定会触发setter。
Array通过定义一个拦截器覆盖
Array.prototype
。之后,每当使用Array原型上的方法操作数组时,其实执行的都是拦截器中提供的方法。
拦截器
拦截器其实就是一个和
Array.prototype
一样的Object,里面包含的属性一模一样,只不过这个Object中某些可以改变数组自身内容的方法是我们处理过的。
Array原型中可以改变数组自身内容的方法有7个,分别是
push、pop、shift、unshift、splice、sort、reverse
。
有了拦截器之后,想要让它生效,就需要使用它去覆盖
Array.prototype
。但是我们又不能直接覆盖,因为这样会污染全局的Array,这并不是我们希望看到的结果,我们希望拦截操作只针对那些被侦测了变化的数据生效。也就是说希望拦截器只覆盖那些响应式数据的原型而将一个数据转换成响应式的,需要通过
Observer
,所以我们只需要在Observer中使用拦截器覆盖那些即将转换成响应式Array类型数据的原型。做法就是 ***将拦截器(加工后具备拦截功能的arrayMethods)赋值给
value.__proto__
,通过
__proto__
可以很巧妙地实现覆盖value原型的功能***
// 我们平时有看到这样代码
arr.__proto__ = Array.prototype;
// 这是不是就可以认为覆盖了Array原型身上的功能,arr也可以使用Array原型上的功能 (不知道说的对不对)
虽然绝大多数浏览器都支持这种非标准的属性来访问原型,但是并不是所有的浏览器都支持!因此,我们需要处理不能使用
__proto__
的情况——**Vue直接将
拦截器arrayMethods
身上的这些方法设置到被侦测的数组上。**
我们已经有了拦截器,当数据发生变化,被拦截器拦截,那之后我们通知谁呢?前面Object时是触发setter之后,是通知Dep中的依赖(Watcher),但是在Array中依赖是怎么收集呢?收集之后又通知谁呢?
简单回顾下Object是如何收集依赖?——是在
definReactive
中的getter里使用Dep收集的,每个key都会有一个对应的Dep列表来存储依赖。**简单的说在
getter
中收集依赖,依赖存储在Dep里。**
其实**数组也是在
getter
中收集依赖,在
拦截器
中触发依赖。但这个getter收集到的依赖我们该保存到哪里呢,这个就很关键?它必须getter和拦截器都能够访问到。
所以我们把依赖保存到Observer实例上的dep中**,是因为在getter中可以访问到Observer实例,同时在Array拦截器中也可以访问到Observer实例。
当侦测到数据发送变化时,会向依赖发送通知。此时,首先要能访问到依赖——
通过数组__ob__属性拿到Observe实例,然后就可以拿到__ob__上的dep了
。拿到dep属性之后,通过
ob.dep.notify()
去通知依赖(Watcher)数据发生了改变。
__ob__
的作用不仅仅是为了在拦截器中访问Observer实例这么简单,还可以用来标记当前value是否已经被Observer转换成响应式数据。也就是说,被侦测了变化的数据身上都会有一个
__ob__
属性来表示它们是响应式的。如果value是响应式的,则直接返回
__ob__
;如果不是响应式的,则使用
new Observer
来将数据转换成响应式数据。
侦测新增元素的变化
Observer类不光能处理Object类型的数据,还可以处理Array类型的数据。
如果数组有新增的数据,我们需要把新增的内容转换为响应式来侦测变化,否则就会出现修改数据时无法触发消息等问题。
实现步骤:
1、我们需要在拦截器中对数组方法的类型进行判断。如果操作数组的方法是
push、unshift和splice(可以新增数组元素的方式)
,则把参数中新增的元素拿过来,暂存在
inserted
中,接下来,我们要使用Observer把
inserted
中的元素转换成响应式的。
2、使用
ob.observerArray
来侦测这些新增元素的变化
二、关于Array的问题
Array的变化侦测是通过拦截原型的方式实现的。但是有些数组操作Vue.js是拦截不到的。例如:
1、修改数组中第一个元素的值,无法侦测到数组的变化;
2、清除数组操作也无法侦测到数组的变化。
三、总结
Array追踪变化的方式和Object不一样,我们是通过创建拦截器去覆盖数组原型的方式来追踪变化。
第4章 变化侦测相关的PAI实现原理
vm.$watch
用于观察一个表达式或computed函数在vue.js实例上的变化vm.$set
这个方法主要用来避开Vue.js不能侦测属性被添加的限制vm.$delete
删除数据中的某个属性
如有写的不对的地方,欢迎来讨论
脚踏实地,一步一步来!!!
版权归原作者 *neverGiveUp* 所有, 如有侵权,请联系我们删除。