You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
constqueue: Array<Watcher> = []
let has: {[key: number]: ?true} = {}
let waiting = false
let flushing = false
/**
* Push a watcher into the watcher queue.
* Jobs with duplicate IDs will be skipped unless it's
* pushed when the queue is being flushed.
*/
export function queueWatcher (watcher: Watcher) {constid=watcher.id// 确保只会push一次if(has[id]==null){has[id]=trueif(!flushing){queue.push(watcher)} else {// if already flushing, splice the watcher based on its id// if already past its id, it will be run next immediately.leti=queue.length-1while(i>index&&queue[i].id>watcher.id){i--}
queue.splice(i + 1, 0, watcher)
}// queue the flush// 保证只执行一次nextTickif(!waiting){waiting=truenextTick(flushSchedulerQueue)}}}
functionflushSchedulerQueue(){flushing=trueletwatcher,id// Sort queue before flush.// This ensures that:// 1. Components are updated from parent to child. (because parent is always// created before the child)// 2. A component's user watchers are run before its render watcher (because// user watchers are created before the render watcher)// 3. If a component is destroyed during a parent component's watcher run,// its watchers can be skipped./** * 1.组件的更新由父到子;因为父组件的创建过程是先于子的,所以 watcher 的创建也是先父后子,执行顺序也应该保持先父后子。 * 2.用户的自定义 watcher 要优先于渲染 watcher 执行;因为用户自定义 watcher 是在渲染 watcher 之前创建的。 * 3.如果一个组件在父组件的 watcher 执行期间被销毁,那么它对应的 watcher 执行都可以被跳过,所以父组件的 watcher 应该先执行。 */queue.sort((a,b)=>a.id-b.id)// do not cache length because more watchers might be pushed// as we run existing watchers// 这里不缓存队列长度的原因是在 watcher.run() 的时候,很可能用户会再次添加新的 watcherfor(index=0;index<queue.length;index++){watcher=queue[index]if(watcher.before){watcher.before()}id=watcher.idhas[id]=nullwatcher.run()// in dev build, check and stop circular updates.if(process.env.NODE_ENV!=='production'&&has[id]!=null){circular[id]=(circular[id]||0)+1if(circular[id]>MAX_UPDATE_COUNT){warn('You may have an infinite update loop '+(watcher.user
? `in watcher with expression "${watcher.expression}"`
: `in a component render function.`),watcher.vm)break}}}// keep copies of post queues before resetting stateconstactivatedQueue=activatedChildren.slice()constupdatedQueue=queue.slice()resetSchedulerState()// call component updated and activated hookscallActivatedHooks(activatedQueue)callUpdatedHooks(updatedQueue)// devtool hook/* istanbul ignore if */if(devtools&&config.devtools){devtools.emit('flush')}}
/** * Scheduler job interface. * Will be called by the scheduler. */run(){if(this.active){this.getAndInvoke(this.cb)}}getAndInvoke(cb: Function){constvalue=this.get()if(value!==this.value||// Deep watchers and watchers on Object/Arrays should fire even// when the value is the same, because the value may// have mutated.isObject(value)||this.deep){// set new valueconstoldValue=this.valuethis.value=valuethis.dirty=falseif(this.user){try{// 这就是为什么我们写watch时能拿到新值和旧值cb.call(this.vm,value,oldValue)}catch(e){handleError(e,this.vm,`callback for watcher "${this.expression}"`)}}else{cb.call(this.vm,value,oldValue)}}}
执行 watcher.run() ,就是执行 getAndInvoke:
拿到值 value , 如果满足新旧值不等、新值是对象类型、deep 模式任何一个条件,则执行 watcher 的回调
上一篇我们分析依赖收集的过程。那么将这些依赖收集起来有什么用呢?答案是在数据变化时能派发更新。本文就一起通过例子一起去了解派发更新的过程。本文 demo:
还记得之前 defineReactive 中给数据都设置了 getter 和 setter,上一篇依赖收集原理分析了 getter 的过程,在数据发生改变时就会触发 setter 过程。分析源码前,希望你能够使用 vue-cli 运行上面的 demo,并且在 setter 函数中打好断点,一起感受 setter 的过程。
当我们点击按钮改变 text 的值时,便会触发 proxy 的代理,变成设置 this._datas.text 的值,便会触发 setter。其中的关键逻辑:
看到 Dep 类:
这段代码的逻辑比较简单,遍历所有 subs 即 Watcher 实例数组,调用其 update 方法:
把英文注释删掉之后, update 的逻辑非常清晰。这里不是计算属性,所以直接就调 queueWatcher,这个函数定义在 src/core/observer/scheduler.js 中:
这里引入了一个队列的概念,这也是 Vue 在做派发更新的时候的一个优化点,它并不会每次数据改变都触发 watcher 的回调,而是把这些 watcher 先添加到一个队列里,然后在 nextTick 后执行 flushSchedulerQueue。flushSchedulerQueue 定义在 src/core/observer/scheduler.js 中:
flushSchedulerQueue 在做完排序(排序原因在注释中说明)之后,遍历队列做 run 调用。watcher.run() 有个细节要注意一下,每次都会去重新计算队列长度,因为在执行 watcher.run() 的时候可能会有新的 watcher,这时候要重新计算,走 queueWatcher 的过程。queueWatcher 会使 queue 发生改变。
执行 watcher.run() ,就是执行 getAndInvoke:
最后,遍历执行完所有 watcher.run() 后,会执行 resetSchedulerState()
把流程中的一些标志位恢复,并将队列清空。
The text was updated successfully, but these errors were encountered: