vue中的非同步更新佇列和$nextTick的使用
文章目錄
一、前言
我們先來看這樣一個場景:有一個div,預設用 v-if 將它隱藏,點選一個按鈕後,改變 v-if 的值,讓它顯示出來,同時拿到這個div的文字內容。如果一開始 v-if 的值是false,直接去獲取 div的內容是獲取不到的,因為此時 div還沒有被創建出來,那麼應該在點選按鈕後,改變 v-if 的值為 true,div才會被建立,此時再去獲取,示例程式碼如下:
<div id="app">
<div id="div" v-if="isShow">我是一段內容</div>
<button @click="getText">獲取div中的內容</button>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
isShow: false
},
methods: {
getText() {
this.isShow = true;
let text = document.getElementById('div').textContent;
console.log(text);
}
}
});
</script>
但是執行之後控制檯報錯了:
意思就是獲取不到 div 元素(document.getElementById('div')
的結果是 null )。這裡就涉及 Vue 一個重要的概念:非同步更新佇列。
二、非同步更新佇列
Vue並不是在資料發生變化之後立即更新 DOM,而是按一定的策略進行 DOM 的更新。
Vue 在更新 DOM 時是非同步執行的。只要偵聽到資料變化,Vue 將開啟一個佇列,並緩衝在同一事件迴圈中發生的所有資料變更。如果同一個 watcher 被多次觸發,只會被推入到佇列中一次。這種在緩衝時去除重複資料對於避免不必要的計算和 DOM 操作是非常重要的。然後,在下一個的事件迴圈“tick”中,Vue 重新整理佇列並執行實際 (已去重的) 工作。
所以如果我們用一個 for 迴圈來動態改變資料 100次,其實它只會應用最後一次改變,如果沒有這種機制, DOM 就要重繪100次,這固然是一個很大的開銷。
Vue 在內部對非同步佇列嘗試使用原生的 Promise.then、
MutationObserver
和 setImmediate
,如果執行環境不支援,則會採用 setTimeout(fn, 0)
代替。
例如,當我們設定 vm.someData = 'new value'
,該元件不會立即重新渲染。當重新整理佇列時,元件會在下一個事件迴圈“tick”中更新。多數情況我們不需要關心這個過程,但是如果你想基於更新後的 DOM 狀態來做點什麼,這就可能會有些棘手。雖然 Vue.js 通常鼓勵開發人員使用“資料驅動”的方式思考,避免直接接觸 DOM,但是有時我們必須要這麼做。
為了在資料變化之後等待 Vue 完成更新 DOM,可以在資料變化之後立即使用 Vue.nextTick(callback)
(全域性方法) 或者 vm.$nextTick( [callback] )
(在元件內使用)。這樣回撥函式將在 DOM 更新完成後被呼叫。
所以我們可以把上邊的程式碼改為:
getText() {
this.isShow = true;
this.$nextTick(function () {
let text = document.getElementById('div');
console.log(text);
})
}
在元件內使用 vm.$nextTick()
例項方法不需要全域性 Vue,並且回撥函式中的 this 將自動繫結到當前的 Vue 例項上。