this.$nextTick
是 Vue.js 内部使用的一个方法,用于在下一个 DOM 更新循环结束之后执行回调函数。
原理:
- nextTick 方法被调用后,会将回调函数存储在一个队列中
- Vue.js 会利用浏览器的异步队列机制,在 DOM 更新循环结束后执行这个队列中的所有回调函数。
源码:
/**
* Defer a task to execute it asynchronously.
*/
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]()
}
}
// 判断浏览器是否支持Promise,如果支持,则使用Promise的方式实现nextTick
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(nextTickHandler)
}
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// Use MutationObserver where native Promise is not available,
// e.g. PhantomJS, iOS7, Android 4.4
let counter = 1
const observer = new MutationObserver(nextTickHandler)
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
} else {
// Fallback to setTimeout
timerFunc = () => {
setTimeout(nextTickHandler, 0)
}
}
return function queueNextTick (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, reject) => {
_resolve = resolve
})
}
}
})()
在这个方法中,首先定义了一个 callbacks 数组用于存储回调函数,以及一个 pending 标志用于判断是否有回调函数还未执行。然后根据浏览器支持的异步机制,决定使用哪一种方式来实现 nextTick。
如果浏览器支持 Promise,就使用 Promise 的方式,创建一个 resolved 状态的 Promise 对象 p,并利用 p 的 then 方法在下一个 microtask 中执行 nextTickHandler 方法。
如果浏览器支持 MutationObserver,也是创建一个 promise,然后通过改变文本节点的值来触发 MutationObserver,并在触发回调函数时执行 nextTickHandler 方法。
如果浏览器都不支持,则回退到使用 setTimeout。
最后,通过闭包返回一个函数 queueNextTick,该函数接受一个回调函数 cb 和一个上下文对象 ctx,将回调函数包装成一个新的函数,并将其推入 callbacks 数组中。如果当前没有其他回调函数正在执行,则通过 timerFunc 来触发回调函数的执行。
如果在调用 nextTick 时没有传入回调函数,则返回一个 promise 对象。