Vue原始碼小問答二:Vue.nextTick的用法
阿新 • • 發佈:2018-11-28
Vue.nextTick
官方說明
在下次 DOM 更新迴圈結束之後執行延遲迴調。在修改資料之後立即使用這個方法,獲取更新後的 DOM。
註解
Vue的Dom樹更新一般是在非同步回撥中完成的,這裡的非同步回撥可能用promise或者setTimeout。這就導致我們無法以同步的方式獲取更新後的Dom結構,因為在資料狀態更新完成之後Vue並不是立即更新Dom樹,而是在下一個事件迴圈中更新。(不要問我問什麼要做非同步,文件裡說的很清楚)
Vue會在完成Dom更新後呼叫Vue.nextTick
,所以這個可以獲取到更新後的Dom。
nextTick
是一個全域性方法,也就是所有的Vue例項都會在統一的地方處理。這個方法主要是在資料狀態發生變化後呼叫,例如data
mutation
呼叫之後等。例如頁面上有Vue例項a、b,js對這兩個例項的data進行修改後分別呼叫了vue.nextTick
,在頁面完成dom更新後兩個回撥函式會以此被呼叫。
原始碼解毒:請看中文註釋部分
export const nextTick = (function () {
// 存放一次dom更新迴圈內所有的回撥函式
const callbacks = []
// 在一次dom更新迴圈內timerFunc方法只能被呼叫一次
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]()
}
}
// 在eventloop大迴圈中插入回撥
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) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// use MutationObserver where native Promise is not available,
// e.g. PhantomJS IE11, iOS7, Android 4.4
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 {
// fallback to setTimeout
/* istanbul ignore next */
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()
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise((resolve, reject) => {
_resolve = resolve
})
}
}
})()