關於js的event loop知識點,可閱讀JavaScript併發模型與Event Loop,這裏就不着重介紹。
在此,再推薦一篇關於event loop的文章Tasks, microtasks, queues and schedules。html
好了,在對js的event loop有必定了解後,咱們就來進入今天的主題,關於vue的nextTick。vue
在vue中,數據監測都是經過Object.defineProperty來重寫裏面的set和get方法實現的,vue更新DOM是異步的,每當觀察到數據變化時,vue就開始一個隊列,將同一事件循環內全部的數據變化緩存起來,等到下一次event loop,將會把隊列清空,進行dom更新,內部使用的microtask MutationObserver來實現的。html5
雖然數據驅動建議避免直接操做dom,但有時也不得不須要這樣的操做,這時就該Vue.nextTick(callback)
出場了,它接受一個回調函數,在dom更新完成後,這個回調函數就會被調用。無論是vue.nextTick仍是vue.prototype.$nextTick都是直接用的nextTick這個閉包函數。git
export const nextTick = (function () {
const callbacks = []
let pending = false
let timerFunc
function nextTickHandler () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
//other code
})()複製代碼
callbacks就是緩存的全部回調函數,nextTickHandler就是實際調用回調函數的地方。github
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve()
var logError = err => { console.error(err) }
timerFunc = () => {
p.then(nextTickHandler).catch(logError)
if (isIOS) setTimeout(noop)
}
} else if (typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
var counter = 1
var observer = new MutationObserver(nextTickHandler)
var textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
} else {
timeFunc = () => {
setTimeout(nextTickHandle, 0)
}
}複製代碼
爲讓這個回調函數延遲執行,vue優先用promise來實現,其次是html5的MutationObserver,而後是setTimeout。前二者屬於microtask,後一個屬於macrotask。下面來看最後一部分promise
return function queueNextTick(cb?: Function, ctx?: Object) {
let _resolve
callbacks.push(() => {
if (cb) cb.call(ctx)
if (_resolve) _resolve(ctx)
})
if (!pending) {
pending = true
timerFunc()
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}複製代碼
這就是咱們真正調用的nextTick函數,在一個event loop內它會將調用nextTick的cb回調函數都放入callbacks中,pending用於判斷是否有隊列正在執行回調,例若有可能在nextTick中還有一個nextTick,此時就應該屬於下一個循環了。最後幾行代碼是promise化,能夠將nextTick按照promise方式去書寫(暫且用的較少)。緩存
nextTick就這麼多行代碼,但從代碼裏面能夠更加充分的去理解event loop機制。bash
vue閉包