全面解析Vue中的$nextTick
當在代碼中更新了數據,并希望等到對應的Dom更新之后,再執(zhí)行一些邏輯。這時,我們就會用到$nextTick
funcion callback(){
//等待Dom更新,然后搞點事。
}
$nextTick(callback);
官方文檔對nextTick的解釋是:
在下次 DOM 更新循環(huán)結束之后執(zhí)行延遲回調。在修改數據之后立即使用這個方法,獲取更新后的 DOM。
那么,Vue是如何做的這一點的,是不是在調用修改Dom的Api之后(appendChild, textContent = "xxxxx" 諸如此類),調用了我們的回調函數?
實際上發(fā)生了什么呢。
源碼
nextTick的實現邏輯在這個文件里:
vue/src/core/util/next-tick.js
我們調用的this.$nextTick實際上是這個方法:
export function nextTick (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 => {
_resolve = resolve
})
}
}
可以看到
- 回調函數被存放到了一個數組里:callbacks。
- 如果沒有傳遞回調函數,這個方法會返回一個Promise,然后吧reslove當成回調函數放到flushCallbacks中。所以文檔解釋了把本該當成回調函數的callbacks放到then里的用法。
- 然后,有一個變量叫pending,如果不在pending中,則執(zhí)行函數timerFunc。而且pending默認等于false。
- flushCallbacks這個函數會一口氣執(zhí)行所有回調函數。
timerFunc
timerFunc定義在這里
可以看到timerFunc是在一個已resolve了的Promise的then 中執(zhí)行了flushCallbacks.
利用了js事件循環(huán)的微任務的機制
所以,每當我們調用$nextTick,如果pending為false,就會調用timerFunc,然后timerFunc會把flushCallbacks給塞到事件循環(huán)的隊尾,等待被調用。
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
}
}
flushCallbacks
然后在這個文件里還有一個函數叫:flushCallbacks
用來把保存的回調函數給全執(zhí)行并清空。
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
pending
什么時候pending為true呢?
從timerFunc被調用到flushCallbacks被調用期間pending為true
即一個事件循環(huán)周期
在pending期間加入的回調函數,會被已經等待執(zhí)行的flushCallbacks函數給執(zhí)行。
核心機制
看完源碼,發(fā)現除了利用了一個微任務的機制,和Dom更新一點關系都沒有哇。
其實調用nextTick的不僅是開發(fā)者,Vue更新Dom時,也用到了nextTick。
開發(fā)者更新綁定的數據之后,Vue就會立刻調用nextTick,把更新Dom的回調函數作為微任務塞到事件循環(huán)里去。
于是,在微任務隊列中,開發(fā)者調用的nextTick的回調函數,就一定在更行Dom的回調函數之后執(zhí)行了。
但是問題又來了,根據瀏覽器的渲染機制,渲染線程是在微任務執(zhí)行完成之后運行的。渲染線程沒運行,怎么拿到Dom呢?
因為,渲染線程只是把Dom樹渲染成UI而已,Vue更新Dom之后,在Dom樹里,新的Dom節(jié)點已經存在了,js線程就已經可以拿到新的Dom了。除非開發(fā)者讀取Dom的計算屬性,觸發(fā)了強制重流渲染線程才會打斷js線程。
總結
- 首先timerFunc函數負責把回調函數們都丟到事件循環(huán)的隊尾
- 然后,nextTick函數負責把回調函數們都保存起來。
- 調用nextTick函數時會調用timerFunc函數
- Vue更新Dom也會使用nextTick,而且在開發(fā)者調用nextTick之前。
- 因為4中的先后關系和事件循環(huán)的隊列性質,確保了開發(fā)者的nextTick的回調一定在Dom更新之后
以上就是解析Vue中的$nextTick的詳細內容,更多關于Vue中的$nextTick的資料請關注腳本之家其它相關文章!
相關文章
快速解決Error: error:0308010C:digital envelope ro
因為 node.js V17版本中最近發(fā)布的OpenSSL3.0, 而OpenSSL3.0對允許算法和密鑰大小增加了嚴格的限制,下面通過本文給大家分享快速解決Error: error:0308010C:digital envelope routines::unsupported的三種解決方案,感興趣的朋友一起看看吧2024-02-02
Vue 3 中使用 OpenLayers 實時顯示 zoom 
本文介紹了如何在 Vue 3 中使用 OpenLayers 來獲取地圖的 zoom 值以及四角坐標信息,并實時更新數據,這種方式可以用于 GIS 應用開發(fā),幫助用戶更好地了解當前地圖范圍,感興趣的朋友一起看看吧2025-04-04
vue-cli結合Element-ui基于cropper.js封裝vue實現圖片裁剪組件功能
這篇文章主要介紹了vue-cli結合Element-ui基于cropper.js封裝vue實現圖片裁剪組件功能,本文圖文并茂給大家介紹的非常詳細,需要的朋友可以參考下2018-03-03
vue element upload組件 file-list的動態(tài)綁定實現
這篇文章主要介紹了vue element upload組件 file-list的動態(tài)綁定實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10

