Vue的dom更新機制 & Vue的nextTick


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM