浅谈vue的第一个commit分析(5)

在上面我们可以发现,watch在收到信息更新执行update时。如果非同步情况下会执行pushWatcher(this)将实例推入执行池中,那么在何时会执行回调函数,如何执行呢?我们一起看看pushWatcher的实现。

// batch.js var queueIndex var queue = [] var userQueue = [] var has = {} var circular = {} var waiting = false var internalQueueDepleted = false // 重置执行池 function resetBatcherState () { queue = [] userQueue = [] // has 避免重复 has = {} circular = {} waiting = internalQueueDepleted = false } // 执行执行队列 function flushBatcherQueue () { runBatcherQueue(queue) internalQueueDepleted = true runBatcherQueue(userQueue) resetBatcherState() } // 批量执行 function runBatcherQueue (queue) { for (queueIndex = 0; queueIndex < queue.length; queueIndex++) { var watcher = queue[queueIndex] var id = watcher.id // 执行后置为null has[id] = null watcher.run() // in dev build, check and stop circular updates. if (process.env.NODE_ENV !== 'production' && has[id] != null) { circular[id] = (circular[id] || 0) + 1 if (circular[id] > config._maxUpdateCount) { warn( 'You may have an infinite update loop for watcher ' + 'with expression "' + watcher.expression + '"', watcher.vm ) break } } } } // 添加到执行池 export function pushWatcher (watcher) { var id = watcher.id if (has[id] == null) { if (internalQueueDepleted && !watcher.user) { // an internal watcher triggered by a user watcher... // let's run it immediately after current user watcher is done. userQueue.splice(queueIndex + 1, 0, watcher) } else { // push watcher into appropriate queue var q = watcher.user ? userQueue : queue has[id] = q.length q.push(watcher) // queue the flush if (!waiting) { waiting = true // 在nextick中执行 nextTick(flushBatcherQueue) } } } }

4. patch实现

上面便是vue中数据驱动的实现原理,下面我们接着回到主流程中,在执行完watch后,便执行this._update(this._watcher.value)开始节点渲染

// _update => createPatchFunction => patch => patchVnode => (dom api) // vtree是通过compile函数编译的render函数执行的结果,返回了当前表示当前dom结构的对象(虚拟节点树) _update (vtree) { if (!this._tree) { // 第一次渲染 patch(this._el, vtree) } else { patch(this._tree, vtree) } this._tree = vtree } // 在处理节点时,需要针对class,props,style,attrs,events做不同处理 // 在这里注入针对不同属性的处理函数 const patch = createPatchFunction([ _class, // makes it easy to toggle classes props, style, attrs, events ]) // => createPatchFunction返回patch函数,patch函数通过对比虚拟节点的差异,对节点进行增删更新 // 最后调用原生的dom api更新html return function patch (oldVnode, vnode) { var i, elm, parent var insertedVnodeQueue = [] // pre hook for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]() if (isUndef(oldVnode.sel)) { oldVnode = emptyNodeAt(oldVnode) } if (sameVnode(oldVnode, vnode)) { // someNode can patch patchVnode(oldVnode, vnode, insertedVnodeQueue) } else { // 正常的不复用 remove insert elm = oldVnode.elm parent = api.parentNode(elm) createElm(vnode, insertedVnodeQueue) if (parent !== null) { api.insertBefore(parent, vnode.elm, api.nextSibling(elm)) removeVnodes(parent, [oldVnode], 0, 0) } } for (i = 0; i < insertedVnodeQueue.length; ++i) { insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]) } // hook post for (i = 0; i < cbs.post.length; ++i) cbs.post[i]() return vnode }

结尾

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/73c8abf933bcb843693813e7c84e22f9.html