vue-数据监控与依赖收集
文章分析如下demo{{a.b}}{{c}}{{d}}
<脚本>varapp=newVue({el:"#app",data:function(){return{a:{b:1},c:1}},watch:{'a.b':function(){console.log(22)}},computed:{d:function(){returnthis.c}}});数据监控:vue从initData监控data数据的步骤总结如下步骤:1.使用initData初始化data数据2.将数据挂载到vm._data(后面会把_data赋值给$data,这里的data如果是函数,就是返回的数据)3.监控数据通过observe4,如果数据是非对象类型的数据(typeof==object,且不为null),终止程序5,在observe中使用newObserver生成ob6,使用`this.dep=newDep()`在Observer中挂载dep7,Observer将__ob__指向这个,所以可以使用`__ob__.dep`找到dep8,遍历属性,使用defineReactive监控属性9,g在defineReactive中enerate`vardep=newDep();`,这个dep是一个闭包10.使用defineReactive中observe监控属性表示的值,即第3步。至此循环递归依赖于收集三种手表er1、normal-watcher(watch中的数据,由initWatch生成)记录在_watchers中(所有的watch都会存储在这里)2、computed-watcher(计算的)记录在_computedWatchers中3、render-watcher是vm。_watchernormal-watcher:要运行initWatch,我们在组件钩子函数watch中定义,都属于这个类型,即只要被监控的属性发生变化,就会触发定义的回调函数。这类watch的表达式是计算属性中的属性名watch在初始化(initWatch)时会调用vm.$watch函数,vm.$watch函数会直接使用Watcher构建观察者目的。watch中属性的值作为watcher.cb存在,更新watcher时在watcher.run函数中执行。watch中属性的key会经过parsePath处理,parsePath返回的函数会用来获取watch的初始值。比如a.b被解析为监听a中的b属性,这样就会先查找a,即触发a的get。1、将cb赋给a.b的值,将expression赋给a.b2。使用parsePath解析expOrFn,即a.b,将返回的函数赋值给watcher实例的getter,即this.getter3。运行this.get获取a.b的值。对于依赖收集,this指向a.b的watcher实例。4.运行pushTarget将Dep.target指向watcher实例。5.先运行this.getter获取a,在defineReactive中运行get6,运行dep.depend(此时的dep是指data.a的dep在闭包中),然后运行Dep.target.addDep,将data.a的dep添加到watcher实例中,并将watcher实例追加到data.a的dep.subs中,因为a有__ob__,所以会运行a.__ob__.dep.depend,将a的dep添加到watcher实例,并将watcher实例附加到a的dep.subs中7.使用获取的a获取属性b8,运行dep.depend(此时的dep是指a.b的dep,在闭包中),以及然后运行Dep.target.addDep,将a.b的dep添加到watcher实例中,并将watcher实例Append到a.b的dep.subs中,因为b没有__ob__,所以不会继续追加9。此时point,得到a.b的值1,将这个值赋值给watcher实例的valuecomputed-watcher:运行initComputed,我们在componenthook函数computed中定义的都属于这个ty体育课每个计算属性最终都会生成一个对应的watcher对象,但是这种watcher有一个特点:当计算属性依赖其他数据时,该属性不会立即重新计算,只有后面需要在别处读取该属性时,它才会实际上计算,也就是它具有惰性(lazycalculation)特性。此类手表的表达式是计算属性中的属性名称。在初始化computed(initComputed)的时候,会先生成一个watch实例,然后在data或者props上监听数据是否已经存在,如果存在则抛出警告,否则会调用defineComputed函数来监听数据,并为组件中的属性绑定getter和属性。二传手。注意:computed中的属性是直接绑定vm的,所以如果写a.d,属性名是a.d,而不是a对象的属性d。1、执行initComputed,遍历computed生成一个watch实例,挂载到vm._computedWatchers上(1)将cb赋给一个空函数,将expression赋给字符串形式的expOrFn(d的值,函数或对象的get),andassignthis.getterisexpOrFn(2)默认的computed设置lazy为true,不会运行this.get获取值,所以这里生成了watch实例。2.执行defineComputed函数。如果d的值是一个函数,或者d的cache属性不为false,那么就会使用createComputedGetter函数生成computedGetter函数作为d的getter函数。如果cache设置为false,则不会被createComputedGetter封装。会运行get,d的setter是它的set或者empty函数(默认)3.当获取到d的值时(比如rendering,此时Dep.target是renderingwatcher),会运行computedGetter函数4.根据watcher.dirty的值决定是否运行watcher.evaluate重新获取属性值,这是惰性计算的关键。dirty的值默认为true,依赖发生变化或更新时变为true,evaluate后变为false(1)在watcher.evaluate中运行this.get获取d的值,收集依赖。this指向d的watcher实例(2)运行pushTarget将Dep.target指向d的watcher实例(3)运行this.getter,它会先获取this.c的值,在defineReactive(4)中运行get)运行dep.depend(此时dep指的是data.c的dep,在闭包中),然后运行Dep.target.addDep,将data.c的dep追加到d的watcher实例中,并appendd的watcher实例到data.c的dep.subs中将(5)d中的watcher出栈,重置Dep.target渲染watcher5,运行watcher.depend,遍历watcher.deps(这里主要是depofdata.c),并将它们与renderingwatcher关联起来注意:computed中的数据不被Observer监控,所以没有deprender-watcher:运行mountComponent挂载组件。每个组件都有一个渲染观察器。当data/computed中的属性发生变化时,将调用render-watcher来更新组件。看法。这类watch的表达式是function(){vm._update(vm._render(),hydrating);}。1.生成updateComponent函数,2.实例化一个渲染观察器,并将updateComponent作为expOrFn参数传递。3.将cb赋值给一个空函数,将expression赋值给字符串形式的updateComponent,将this.getter赋值给expOrFn4,运行this.get,收集依赖5.运行pushTarget将Dep.target指向renderingwatcher实例6.运行this.getter,即updateComponent函数7.使用render函数生成一个vnode,作为第一个参数传递给_update8,render函数会对使用到的变量进行getter操作,完成依赖收集(一)获取a,将data.a的dep追加到renderingwatcher实例,将renderingwatcher实例追加到data.a的dep。在subs(2)获取a.b,将a.b的dep添加到renderingwatcher实例中,并将renderingwatcher实例追加到a.b的dep.subs中(3)获取c,将data.c的dep追加到rendering中watcher实例,将renderingwatcher实例添加到data.c的dep.subs中(4)获取d,运行d的getter函数computedGetter(详见上述computed-watcher中的步骤3-5)9.完成dependencies收集后,如果变量被修改,会触发dep.notify通知renderingwatcher实例的update操作并重新渲染