几个与变化侦测相关的常用API的内容原理

vm.$watch

vm.$watch(expOrFn,callback,[options])
【参数】:
1.{string|Function} expOrFn
2.{Function|Object} callback
3.{Object} [options] =>{boolean} deep =>{boolean} immediate
【返回值】:{Function} unwatch
【用法】:用于观察一个表达式或computed函数在Vue.js实例上的变化。回调函数调用时,会从参数得到新数据(new value)和旧数据(old value)。表达式只接受以点分隔的路径,例如a.b.c。如果是一个比较复杂的表达式,可以用函数替代表达式。

vm.$watch('a.b.c',function(newVal,oldVal){
    //做点什么
})

vm.$watch返回一个取消观察的函数,用来停止触发回调。

var unwatch = vm.$watch('xxx',function(){})
//取消观察
unwatch()

deep:为了发现对象内部值的变化,可以在选项参数中指定deep:true

vm.$watch('obj',function(){},{
    deep:true
})
vm.obj.value = '123';

*监听数组的变动不需要这么做
immediate:在选项参数中指定immediate:true,将立即以表达式的当前值触发回调

vm.$watch('a',function(){},{
    immediate:true    
})
//立即以'a'的当前值触发回调

watch的内部原理

vm.$watch实际上是对Watcher的一种封装,但是deep和immediate是Watcher中没有的。
vm.$watch执行new Watcher来实现基本功能
expOrFn是支持函数的,新增对于expOrFn的判断,如果是函数,直接将值赋给getter,如果不是函数,则使用parsePath函数来读取keypath中的数据,keypath是属性路径,例如a.b.c,说明从vm.a.b.c中读取数据。
当时函数时,不只可以动态返回数据,读取的所有数据都会被Watcher观察,如果是字符串则会读取这个keypath所指向的数据并观察变化,函数则会同时观察函数中读取的所有Vue.js实例上的响应式数据。
最后函数返回一个unwatch用来取消观察数据,事实上执行这个函数时,其实时执行了Watcher.teardown()来取消观察数据,本质是把Watcher实例从当前正在观察的状态的依赖列表中移除。teardown其实就是需要在Watcher中记录自己订阅了谁,那些dep收集了watcher实例,当要取消订阅的时候,则循环自己记录的订阅列表来通知Dep把自己从Dep的依赖列表中移除。

deep参数实现原理

其实就是除了要触发当前监听数据的收集依赖的逻辑之外,还要把当前监听的这个数据在内的所有子值都触发一遍收集依赖,然后当这个依赖的所有子数据发生变化的时候就会通知当前Watcher了。
如果用户设置了deep参数,则用一个叫做traverse的函数在window.target=undefined之前来处理deep逻辑。如果不在这个语句之前去触发子值的收集依赖,不能保证子集收集的依赖是当前的Watcher
traverse函数:先判断val的类型,如果不是数组或者是对象就直接返回什么都不干,如果是拿到val的dep.id来保证不重复收集依赖,如果是数组,则循环每一项递归调用_traverse,如果是对象,就循环所有key,执行一次读取操作,再递归子值。

vm.$set

vm.$set(target,key,value)
【参数】:
1.{Object|Array} target
2.{String|Number} key
3.{any} value
【返回值】:{Function} unwatch
【用法】:在object上设置一个属性,如果object是响应式的,vue会保证属性被创建后也是响应式的,并且触发视图的更新。这个方法主要是用来避开Vue.js不能侦测属性被添加后的限制。
*注意:target不能是Vue.js实例(使用target._isVue来判断)或者Vue.js实例的根数据对象(使用ob.vmCount来判断)。

vm.$set用来解决之前无法侦测新增属性的问题,例如直接使用this.obj.name=xxx来添加属性

vm.$delete

delete语句删除的的属性,Vue无法侦测到数据发生了变化,使用vm.$delete来删除数据中的某个属性,此时就可以侦测到数据发生的变化

vm.$delete(target,key)
【参数】:
1.{Object|Array} target
2.{String|Number} key/index
【用法】:删除对象的属性。如果对象是响应式的,需要确保删除能触发更新视图。这个方法主要用于避开Vue.js不能检测到属性被删除的限制。

和set差不多,就是删除查找到对应的索引位置使用splice方法删除然后通知依赖,找不到数据就什么都不做,找到了不是响应式就删除就行,如果是就通知依赖。