持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天。
用法
这个是官方的介绍用法:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
// DOM 更新了
})
Event Loop
怎么做到的呢?
首先我们要了解JavaScript
的Event Loop
,简单来讲就是下面几个步骤:
- 所有同步任务都在主线程上执行,形成一个执行栈。
- 主线程外,存在一个任务队列。用来收集宏任务和微任务。
- 执行栈中的同步任务执行完毕,系统会读取任务队列,将所有的微任务执行掉,然后再去执行宏任务。
- 主线程不断重复上面的三步。
在浏览器环境,常见的宏任务有setTimeout,MessageChannel,postMessage,setImmediate;常见的微任务有MutationObsever和Promise.then。
也就是说,将nextTick函数中的callback函数添加到微任务或者宏任务中,在下一次宏任务之前,执行掉callback即可。
我们看看精简后的源码是怎么实现的。
源码解析
const callbacks = []
let pending = false
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
let timerFunc
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
}
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
export function nextTick (cb?: Function, ctx?: Object) {
let _resolve
callbacks.push(() => {
if (cb) {
cb.call(ctx)
}
})
if (!pending) {
pending = true
timerFunc()
}
}
三句话解释清楚nextTick源码做了那些事情:
- 根据浏览器环境设置timerFunc函数,利用promise或setImmediate或setTimeout,实现在下一个宏任务前执行fulshCallbacks函数
- nextTick函数用于收集cb函数,收集到callbacks数组;pending控制执行次数,在同一个宏任务中,只执行一次timerFuc函数
- flushCallbacks函数是将所有收集到的cb函数执行掉,并置空callbacks数组,将pending设置为false。