场景:
new Vue({
el: '#app',
render: h => h(App)
})
- 初始化渲染时在根实例的
mountComponent
方法中new Watcher()
在实例化watcher的过程中会调用updateComponent回调方法,这是会调用render函数,在执行render函数的过程中会访问data中的数据,这时会将当前的watcher放到targetStack数组中,此时的Dep.target是当前根实例的watcher。(Dep.target需保持唯一性,是因为同一时间只会有一个 watcher 被计算,Dep.target 就表示正在计算的 watcher)
- 根实例还没渲染完成,会进行App组件的挂载,在组件的mountComponent方法中,也会new Watcher,在执行render函数的过程中由于访问了data中的数据会触发getter,会将组件的渲染watcher添加到targetStack的数组中,此时Dep.target是当前组件的渲染watcher
- 当组件的mountComponent方法执行结束后,会执行popTarget方法,(实际上就是把
Dep.target
恢复成上一个状态,因为当前 vm实例 的数据依赖收集已经完成,那么对应的渲染Dep.target
也需要改变,此时为根实例),从而继续执行根实例的mountComponent,当根实例的mountComponent结束后,会执行popTarget,将Dep.target恢复为上一个状态(此时为undefined) - 当数据发生变化时,会触发setter,setter会执行一个queueWatchcer方法,该方法会把watcher放入一个queue(队列)中(同一个watcher只会添加一次),然后在下一次nextTick后执行flushSchedulerQueue方法,nextTick(flushSchedulerQueue)的调用逻辑也只有一次,flushSchedulerQueue方法中遍历队列(queue),拿到对应watcher,执行watcher.run(),run函数中会执行this.get()方法,对于渲染watcher而言,调用this.get()又会执行mountComponent方法,此时调用render函数时,又会访问data数据,此时又调用了getter方法进行依赖收集,接着重新执行patch过程进行页面的重新渲染,此时页面已经更新完毕,在下一次修改数据时又会根据新收集的依赖进行派发更新
派发更新就是当数据改变后,通知所有订阅了数据变化的watcher执行update。派发更新的过程中会把所有要执行update的watcher插入到队列中,在nextTick后执行flushSchedulerQueue