关于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闭包