Vue NextTick介紹與使用原理
一、NextTick是什么
定義
在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)。在修改數(shù)據(jù)之后立即使用這個方法,獲取更新后的 DOM。
理解
Vue在更新DOM時時異步執(zhí)行的。當(dāng)數(shù)據(jù)發(fā)生變化時,Vue將開啟一個異步更新隊列,視圖需要等隊列中所有數(shù)據(jù)變化完成之后,再統(tǒng)一進(jìn)行更新。
舉例:
<body>
<div id="app">
<div>
<button id="firstBtn" @click="testClick()" ref="aa">{{testMsg}}</button>
</div>
</div>
</body>
</html>
<script>
new Vue({
el:'#app',
data () {
return {
testMsg:"原始值",
}
},
methods:{
testClick:function(){
this.testMsg="修改后的值";
console.log(this.$refs.aa.innerText); //that.$refs.aa獲取指定DOM,輸出:原始值
console.log(document.querySelector('#firstBtn').innerText)
}
}
})
</script>這時候想獲取頁面最新的DOM節(jié)點,卻發(fā)現(xiàn)獲取到的仍然是舊值。

這是因為message數(shù)據(jù)在發(fā)現(xiàn)變化的時候,vue并不會立刻去更新Dom,而是將修改數(shù)據(jù)的操作放在了一個異步操作隊列中,如果我們一直修改相同數(shù)據(jù),異步操作隊列還會進(jìn)行去重,等待同一事件循環(huán)中的所有數(shù)據(jù)變化完成之后,會將隊列中的事件拿來進(jìn)行處理,進(jìn)行DOM的更新。
為什么要有nexttick
{{num}}
for(let i=0; i<100000; i++){
num = i
}
如果沒有 nextTick 更新機(jī)制,那么 num 每次更新值都會觸發(fā)視圖更新(上面這段代碼也就是會更新10萬次視圖),有了nextTick機(jī)制,只需要更新一次,所以nextTick本質(zhì)是一種優(yōu)化策略。
二、使用場景
this.testMsg="修改后的值";
console.log(this.$refs.aa.innerText); //that.$refs.aa獲取指定DOM,輸出:原始值
Vue.nextTick(function(){
console.log(document.querySelector('#firstBtn').innerText)
})

組件內(nèi)使用vm.$nextTick() 實例方法只需要通過this. $nextTick(),并且回調(diào)函數(shù)中的 this 將自動綁定到當(dāng)前的 Vue 實例上
this.message = '修改后的值'
console.log(this.$refs.aa.innerText) // => '原始的值'
this.$nextTick(function () {
console.log(this.$refs.aa.innerText) // => '修改后的值'
})
$nextTick() 會返回一個 Promise 對象,可以是用async/await完成相同作用的事情
await Vue.nextTick()
console.log(document.querySelector('#firstBtn').innerText)
三、實現(xiàn)原理
- callbacks也就是異步操作隊列
- callbacks新增回調(diào)函數(shù)后又執(zhí)行了timerFunc函數(shù),pending是用來標(biāo)識同一個時間只能執(zhí)行一次。
export function nextTick(cb?: Function, ctx?: Object) {
let _resolve;
// cb 回調(diào)函數(shù)會經(jīng)統(tǒng)一處理壓入 callbacks 數(shù)組
callbacks.push(() => {
if (cb) {
// 給 cb 回調(diào)函數(shù)執(zhí)行加上了 try-catch 錯誤處理
try {
cb.call(ctx);
} catch (e) {
handleError(e, ctx, 'nextTick');
}
} else if (_resolve) {
_resolve(ctx);
}
});
// 執(zhí)行異步延遲函數(shù) timerFunc
if (!pending) {
pending = true;
timerFunc();
}
// 當(dāng) nextTick 沒有傳入函數(shù)參數(shù)的時候,返回一個 Promise 化的調(diào)用
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve;
});
}
}- timerFunc函數(shù)定義,這里是根據(jù)當(dāng)前環(huán)境支持什么方法則確定調(diào)用哪個,分別有:Promise.then、MutationObserver、setImmediate、setTimeout;
- 通過上面任意一種方法,進(jìn)行降級操作
export let isUsingMicroTask = false
if (typeof Promise !== 'undefined' && isNative(Promise)) {
//判斷1:是否原生支持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]'
)) {
//判斷2:是否原生支持MutationObserver
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)) {
//判斷3:是否原生支持setImmediate
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
//判斷4:上面都不行,直接用setTimeout
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}- 無論是微任務(wù)還是宏任務(wù),都會放到flushCallbacks使用
- 這里將callbacks里面的函數(shù)復(fù)制一份,同時callbacks置空
- 依次執(zhí)行callbacks里面的函數(shù)
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}小結(jié):
- 把回調(diào)函數(shù)放入callbacks等待執(zhí)行;
- 將執(zhí)行函數(shù)放到微任務(wù)或者宏任務(wù)中;
- 事件循環(huán)到了微任務(wù)或者宏任務(wù),執(zhí)行函數(shù)依次執(zhí)行callbacks中的回調(diào)。
到此這篇關(guān)于Vue NextTick介紹與使用原理的文章就介紹到這了,更多相關(guān)Vue NextTick內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue在自定義組件上使用v-model和.sync的方法實例
自定義組件的v-model和.sync修飾符其實本質(zhì)上都是vue的語法糖,用于實現(xiàn)父子組件的"數(shù)據(jù)"雙向綁定,下面這篇文章主要給大家介紹了關(guān)于vue在自定義組件上使用v-model和.sync的相關(guān)資料,需要的朋友可以參考下2022-07-07
利用Vite2和Vue3實現(xiàn)網(wǎng)站國際化的全過程
vite2已經(jīng)出來一段時間了,最近沒忍住嘗試了一下,這篇文章主要給大家介紹了關(guān)于利用Vite2和Vue3實現(xiàn)網(wǎng)站國際化的相關(guān)資料,需要的朋友可以參考下2021-08-08
vue3.0 proxy設(shè)置代理不成功的問題及解決方案
這篇文章主要介紹了vue3.0 proxy設(shè)置代理不成功的問題及解決方案,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-12-12

