vue.js$nextTick的一個問題?
什麼叫DOM更新後執行,又在數據修改後立即使用這個方法,就不能在修改數據後並且DOM更新後再執行?
簡單說,因為DOM至少會在當前tick裡面的代碼全部執行完畢再更新。所以不可能做到在修改數據後並且DOM更新後再執行,要保證在DOM更新以後再執行某一塊代碼,就必須把這塊代碼放到下一次事件循環裡面,比如setTimeout(fn, 0),這樣DOM更新後,就會立即執行這塊代碼。
//改變數據
vm.message = "changed"
//想要立即使用更新後的DOM。這樣不行,因為設置message後DOM還沒有更新
console.log(vm.$el.textContent) // 並不會得到"changed"
//這樣可以,nextTick裡面的代碼會在DOM更新後執行
Vue.nextTick(function(){
console.log(vm.$el.textContent) //可以得到"changed"
})
HTML: &&
js:
this.testCount=1000;
//1
console.log(document.getElementById("testCount").innerHTML + ": no updated");
for(var j=0;j&<1000;j++){}
setTimeout(function(){
//2
console.log(document.getElementById("testCount").innerHTML+": updated")
},0);
以上是簡單的nextTick的實現。
首先如果你能明白句1和句2 的輸出有何不同,你就已經對nextTick 明白了50%。
// 1 的輸出為
0: no updated
// 2 的輸出為
1000: updated
vue的nextTick的作用類似上面的setTimeout,再添加一個新的空的非同步事件,執行完非同步事件後調用callback。
如果你猜對了這個輸出結果並對js的非同步事件有所了解,那就可以止步於此了,下面的解釋就不用看了。
----------------------------------------------------------------------------------------------------------------------
下面我們再具體聊聊vue的dom更新和非同步函數
DOM更新: 在vue中,你修改了data的某一個值,並不會立即反應到該ele中。vue將你對data的更改放到watcher的一個對列中(非同步),只有在當前任務空閑時才會去執行watcher隊列任務。這就有一個延遲時間了。
所以上面的例子修改了testCount 的值,並不會對ele立即起作用,如果這時候你取ele的值肯定是錯誤的了。
查看vue源碼,會發現vue是通過promise/mutationObserve/setTimeout來實現的。這3個方法有什麼共同點呢?
都是非同步函數,都是向瀏覽器添加了一個空的非同步事件,執行完之後調用callback。
那為什麼我們把想要執行的代碼放在非同步事件的回調里,就可以了呢?
非同步函數:這個講起來真的是要說一大堆廢話。
我還是長話短說的好,非同步事件是不會立即執行的代碼,會被js處理器放到一個隊列里,按照隊列的順序優先順序等一個個按次序執行,新添加的事件都會放在隊列末尾。
已上面的例子來說,
this.testCount=1000; // 創建一個watcher非同步事件1,
console.log(document.getElementById("testCount").innerHTML + ": no updated");//執行,值為0
for(var j=0;j&<1000;j++){}//執行
setTimeout(function(){//再添加一個非同步事件,0s後執行
//2
console.log(document.getElementById("testCount").innerHTML+": updated")
},0);
以上可看出先添加的watcher非同步事件進行dom更新,再添加的setTimeout進行取值操作。因為隊列按順序執行,所以肯定是先執行dom更新在進行callback。
即 DOM更新後執行 .
比如你想讓一個dom元素顯示,然後下一步去獲取這個元素的offsetWidth,最後你獲取到的會是0。
因為你改變數據,把show變成true,元素並不會立即顯,理所當然也不會獲取到動態寬度。
正確的做法是先把元素show出來,在$nextTick去執行獲取寬度的操作,不知道這樣說會不會好理解一點。
openSubmenu() {
this.show = true
//獲取不到寬度
this.$nextTick(() =&>
//這裡才可以
let w = this.$refs.submenu.offsetWidth;
})
}
這是個回調函數,dom更新之後才調用。
推薦閱讀:
TAG:Vuejs |