对象
首先定义一个Observer类把object变成一个可追踪的,然后通过defineReactive方法中的definePropty中的get方法进行一个依赖收集(window.target=watcher),把收集的依赖存储到Dep类中,Dep类中包含了添加方法(addSub)、删除方法(removeSub)、队列更新(notify)等方法。当后续在definPropty设置新值时,就会调用notify方法去更新存储的watcher,此外,还需要存储一个id,以免重复收集依赖。
数组
为了不污染全局的Array.Prototype,只能在Observer中针对那些需要进行需要进行监测的数组进行__protp__来覆盖原型方法,针对不支持__protp__的浏览器直接循环拦截器,把拦截器中的方法设置到数组身上来拦截Array.Prototype中的原生方法
Array收集依赖的方式和Object一样,都是在get中收集。但是由于收集的位置不同,数组要在拦截器中向依赖发消息,所以不能像Object那样保存在defineReactive中,而是要保存在Observer实例上。
在Observer中,我们对每个侦测了变化的数据都标识了印记__ob__,并把this(Observer实例)保存在__ob__上,这主要两个作用,一方面是为了标记数据是否已经做过监测。另一方面可以很方便的获取__ob__。
除了侦测数组变化以外,还需要侦测数组中的数据发生变化,在Observer中判断如果当前被侦测的数据是数组,则调用observerArray方法将数组中的每一个元素转换成响应式的。
watch的原理
watch其实就是对watcher的一种封装,只不过增加了deep和immediate参数,而且,在watch中可以传入函数作为形参,当形参是函数时,我们可以直接把它赋值给get,watcher会同时监测函数内的所有响应式数据,其中任何数据发生变化时,watcher都会收到通知,
computed的原理
computed是定义在vm上的一个特殊的getter方法,之所以特殊,是因为定义getter方法时,get并不是用户提供的函数,而是vue.js内部的一个函数。在代理函数中可以结合Watcher实现缓存与收集依赖等功能。
计算属性会通过Watcher来观察它所有用到的所有属性变化,当这些属性发生变化时,计算属性会将自身的Watcher的dirty属性设置为true,说明自身的返回值变了,然后计算属性会重新计算值与之前的值相互比较,只有不一致的时候才会通知组件的Watcher进行重新渲染操作,且只有在非服务端渲染环境下,计算属性才能缓存。