Vue 實現響應式並不是數據發生變化之后 DOM 立即變化,而是按一定的策略進行 DOM 的更新。
簡單來說,Vue 在修改數據后,視圖不會立刻更新,而是等同一事件循環中的所有數據變化完成之后,再統一進行視圖更新。
同步里執行的方法,每個方法里做的事情組成一個事件循環;接下來再次調用的是另一個事件循環。
nextTick:在下次 DOM 更新循環結束之后執行延遲回調。在修改數據之后立即使用這個方法,會獲取更新后的 DOM。
用法:
//改變數據
vm.message = 'changed'
//想要立即使用更新后的DOM。這樣不行,因為設置message后DOM還沒有更新
console.log(vm.$el.textContent) // 並不會得到'changed'
//這樣可以,nextTick里面的代碼會在DOM更新后執行
Vue.nextTick(function(){
console.log(vm.$el.textContent) //可以得到'changed'
})
圖解:
上圖中第一塊:
1 首先修改數據,這是同步任務。同一事件循環的所有的同步任務都在主線程上執行,形成一個執行棧,此時還未涉及 DOM 。
2 Vue 開啟一個異步隊列,並緩沖在此事件循環中發生的所有數據改變。如果同一個 watcher 被多次觸發,只會被推入到隊列中一次。
上圖中第二塊:
同步任務執行完畢,開始執行異步 watcher 隊列的任務,更新 DOM 。Vue 在內部嘗試對異步隊列使用原生的 Promise.then 和 MessageChannel 方法,如果執行環境不支持,會采用 setTimeout(fn, 0) 代替。
上圖中第三塊:
下次 DOM 更新循環結束之后,此時通過 Vue.nextTick 獲取到改變后的 DOM 。通過 setTimeout(fn, 0) 也可以同樣獲取到。
應用場景:
需要注意的是,在 created 和 mounted 階段,如果需要操作渲染后的試圖,也要使用 nextTick 方法。
官方文檔說明:mounted 不會承諾所有的子組件也都一起被掛載。如果你希望等到整個視圖都渲染完畢,可以用 vm.$nextTick:
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
例一:
showsou(){ // 顯示輸入框 this.showInput = true; // 此時事件循環還沒有結束,dom沒有更新,獲取不到input,不能使他產生焦點 document.getElementById("keywords").focus(); }
可以改為:
showsou(){ this.showInput = true; this.$nextTick(function () { // DOM 更新了 document.getElementById("keywords").focus(); }) }
例二:
<div id="app"> <p ref="myWidth" v-if="showMe">{{ message }}</p> <button @click="getMyWidth">獲取p元素寬度</button> </div> getMyWidth() { this.showMe = true; // 以下代碼報錯 TypeError: this.$refs.myWidth is undefined // this.message = this.$refs.myWidth.offsetWidth; this.$nextTick(()=>{ // dom元素更新后執行,此時能拿到p元素的屬性 this.message = this.$refs.myWidth.offsetWidth; }) }
原文:https://segmentfault.com/a/1190000012861862?utm_source=tag-newest