在vue
中有nextTick
这个API,官方解释,它可以在DOM
更新完毕之后执行一个回调。
一般使用
this.$nextTick(() => {
...
})
一般来说,在对于MVVM
框架结构的技术栈是不推荐操作DOM
的,但是很多情况下可能会需要操作DOM
,特别是一些charts
插件等。
所以nextTick
就出现了,确保我们所操作的DOM是更新之后的。
那VUE是如何知道DOM什么时候更新完了呢?
VUE源码:(把里面的注释都删掉了)
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
let counter = 1
const observer = new MutationObserver(flushCallbacks)
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
步骤:
- 先判断
Promise
- 在判断
MutationObserver
- 在判断
setImmediate
- 最后
setTimeout
整体流程涉及到事件的循环(Event Loo
p)[暂不在这说]
每次event loop
的最后,会有一个UI render
,也就是更新DOM
只要让nextTick
里的代码放在UI render
步骤后面执行,就能访问到更新后的DOM
了。
又涉及到微任务(microtask
)和宏任务(macrotask
)
microtask有:Promise、MutationObserver,以及nodejs中的process.nextTick
macrotask有:setTimeout, setInterval, setImmediate, I/O, UI rendering
每一次事件循环都包含一个microtask队列,在循环结束后会依次执行队列中的microtask并移除,然后再开始下一次事件循环。
vue的nextTick方法的实现原理:
- vue用异步队列的方式来控制DOM更新和nextTick回调先后执行
- microtask因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕
- 因为浏览器和移动端兼容问题,vue不得不做了microtask向macrotask的兼容(降级)方案
网友评论