當在代碼中更新了數據,並但願等到對應的Dom更新以後,再執行一些邏輯。這時,咱們就會用到$nextTickvue
funcion callback(){ //等待Dom更新,而後搞點事。 } $nextTick(callback);
官方文檔對nextTick的解釋是:數組
在下次 DOM 更新循環結束以後執行延遲迴調。在修改數據以後當即使用這個方法,獲取更新後的 DOM。
那麼,Vue是如何作的這一點的,是否是在調用修改Dom的Api以後(appendChild, textContent = "xxxxx" 諸如此類),調用了咱們的回調函數?
實際上發生了什麼呢。瀏覽器
nextTick的實現邏輯在這個文件裏:app
vue/src/core/util/next-tick.js
咱們調用的this.$nextTick其實是這個方法:函數
export function nextTick (cb?: Function, ctx?: Object) { let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true timerFunc() } // $flow-disable-line if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }
能夠看到this
timerFunc定義在這裏線程
能夠看到timerFunc是在一個已resolve了的Promise的then 中執行了flushCallbacks.code
利用了js事件循環的微任務的機制隊列
因此,每當咱們調用$nextTick,若是pending爲false,就會調用timerFunc,而後timerFunc會把flushCallbacks給塞到事件循環的隊尾,等待被調用。事件
if (typeof Promise !== 'undefined' && isNative(Promise)) { const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) } }
而後在這個文件裏還有一個函數叫:flushCallbacks
用來把保存的回調函數給全執行並清空。
function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } }
何時pending爲true呢?
從timerFunc被調用到flushCallbacks被調用期間pending爲true
即一個事件循環週期
在pending期間加入的回調函數,會被已經等待執行的flushCallbacks函數給執行。
看完源碼,發現除了利用了一個微任務的機制,和Dom更新一點關係都沒有哇。
其實調用nextTick的不只是開發者,Vue更新Dom時,也用到了nextTick。
開發者更新綁定的數據以後,Vue就會馬上調用nextTick,把更新Dom的回調函數做爲微任務塞到事件循環裏去。
因而,在微任務隊列中,開發者調用的nextTick的回調函數,就必定在更行Dom的回調函數以後執行了。
可是問題又來了,根據瀏覽器的渲染機制,渲染線程是在微任務執行完成以後運行的。渲染線程沒運行,怎麼拿到Dom呢?
由於,渲染線程只是把Dom樹渲染成UI而已,Vue更新Dom以後,在Dom樹裏,新的Dom節點已經存在了,js線程就已經能夠拿到新的Dom了。除非開發者讀取Dom的計算屬性,觸發了強制重流渲染線程纔會打斷js線程。