Vue.nextTick的原理和用途


 

 

 

 

 

 

 

 

 

 

根據 HTML Standard,在每個 task 運行完以后,UI 都會重渲染,那么在 micro task 中就完成數據更新,當前 task 結束就可以得到最新的 UI 了。反之如果新建一個 task 來做數據更新,那么渲染就會進行兩次。

micro task的這一特性是做隊列控制的最佳選擇,vue進行DOM更新內部也是調用nextTick來做異步隊列控制。而當我們自己調用nextTick的時候,它就在更新DOM的那個micro task后追加了我們自己的回調函數,從而確保我們的代碼在DOM更新后執行。

比如一段時間內,你無意中修改了最初代碼片段中的 msg多次,其實只要最后一次修改后的值更新到DOM就可以了,假如是同步更新的,每次 msg 值發生變化,那么都要觸發 setter->Dep->Watcher->update->patch ,這個過程非常消耗性能。

 

 

 

 

vm.$nextTick( [callback] )

官方解釋:將回調延遲到下次DOM更新循環之后執行。

要理解這句話,首先要了解一下vue的異步更新隊列,vue異步執行dom更新。只要觀察到數據變化,不會立即更新DOM,vue將開啟一個隊列,並緩沖在同一事件循環中發生的所有數據改變。

如果同一個數據被多次改變,只會被推到隊列中一次。例如,當你設置vm.someData =  'new value',對應的dom更新會被推到一個隊列里,該組件不會立即重新渲染,會在當前tick完畢后,在下一個tick中渲染dom。在事件循環中,每進行一次循環操作稱為tick。而nextTick函數就是vue提供的一個實例方法,數據更新后等待下一個tick里dom更新完后執行回調,回調的this自動綁定到調用它的實例上。

例如:

html: 
<span class="test">{{egData}}</span>
<el-button @click="changeData">改變</el-button>

js:
new Vue({
    data () {
        return {
            egData: 'old Message'
        }
    }
    methods: {
        changeData () {
          this.egData = 'new Message'
          console.log($('.test').html(), '-----------------------')
        }
    }
})

結果: 第一次點擊輸出 old Message -----------------------,第二次點擊輸出 new Message -----------------------

使用$nextTick:

js:
new Vue({
    data () {
        return {
            egData: 'old Message'
        }
    }
    methods: {
        changeData () {
          this.egData = 'new Message'
          this.$nextTick(function () {
            console.log($('.test').html(), '-----------------------')
          })

        }
    }
})

結果:不管第幾次點擊,都輸出 new Message -----------------------

 

$nextTick使用場景:

1、數據更新后想要馬上操作新的DOM,需要把操作寫在nextTick的回調里

2、在created鈎子函數里需要操作DOM,也可以把操作寫在nextTick的回調里,(created鈎子函數里還沒有掛載dom,所以直接操作會有問

 

 

 

this.$nextTick()將回調延遲到下次DOM更新循環之后執行。在修改數據之后立即使用它,然后等待DOM更新。它跟全局方法Vue.nextTick一樣,不同的是回調的this自動綁定到調用它的實例上。

 

nextTick的由來:由於VUE的數據驅動視圖更新,是異步的,即修改數據的當下,視圖不會立刻更新,而是等同一事件中的所有數據變化完成之后,再統一進行視圖更新。

nextTick的觸發時機:在同一事件循環中的數據變化后,DOM完成更新,立即執行nextTick(callback)內的回調。

應用場景:需要在視圖更新之后,基於新的視圖進行操作。

以上出現了事件循環的概念,其涉及到JS的運行機制,包括主線程的執行棧、異步隊列、異步API、時間循環的寫作。大致可以理解為:主線程完成同步環境執行,查詢任務隊列,提取隊首的任務,放入主線程中執行;執行完畢,再重復該操作,該過程稱為事件循環。而主線程的每次讀取任務隊列操作,是一個事件循環的開始。異步callback不可能處在同一事件循環中。

簡單總結事件循環:同步代碼執行→查找異步隊列,推入執行棧,執行callback[事件循環1]→查找異步隊列,推入執行棧,執行callback2[事件循環2]...

即每個異步callback,最終都會形成自己獨立的一個事件循環。

結合nextTick的由來,可以推出每個事件循環中,nextTick觸發的時機:同一事件循環中的代碼執行完畢→DOM更新→nextTick callback觸發

ps:上文中的任務隊列、消息隊列、異步隊列指向同一個東西,均指macrotask queue。

 

用法:在下次DOM更新循環結束之后執行延遲回調。在修改數據之后立即使用這個方法,獲取更新后的DOM。

 

  • 在Vue生命周期的created()鈎子函數進行的DOM操作一定要放在Vue.nextTick()的回調函數中

created()鈎子函數執行的時候DOM 其實並未進行任何渲染,而此時進行DOM操作無異於徒勞,所以此處一定要將DOM操作的js代碼放進Vue.nextTick()的回調函數中。與之對應的就是mounted()鈎子函數,因為該鈎子函數執行時所有的DOM掛載和渲染都已完成,此時在該鈎子函數中進行任何DOM操作都不會有問題 。

  • 在數據變化后要執行的某個操作,而這個操作需要使用隨數據改變而改變的DOM結構的時候,這個操作都應該放進Vue.nextTick()的回調函數中


 
 
為了在數據變化之后等待 Vue 完成更新 DOM ,可以在數據變化之后立即使用 Vue.nextTick(callback) 。這樣回調函數在 DOM 更新完成后就會調用。
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>nextTick</title>
        <script src="./js/vue.min.js"></script>
    </head>
    <body>
        
        <div class="app">
          <div ref="msgDiv">{{msg}}</div>
          <div v-if="msg1">Message got outside 1 $nextTick: {{msg1}}</div>
          <div v-if="msg2">Message got inside 2 $nextTick: {{msg2}}</div>
          <div v-if="msg3">Message got outside 3 $nextTick: {{msg3}}</div>
          <button @click="changeMsg">
            Change the Message
          </button>
        </div>
        <script>
            new Vue({
              el: '.app',
              data: {
                msg: 'Hello Vue.',
                msg1: '',
                msg2: '',
                msg3: ''
              },
              methods: {
                changeMsg() {
                  this.msg = "Hello world."
                  this.msg1 = this.$refs.msgDiv.innerHTML
                  this.$nextTick(() => {
                    this.msg2 = this.$refs.msgDiv.innerHTML
                  })
                  this.msg3 = this.$refs.msgDiv.innerHTML
                }
              }
            })

        
        </script>
    </body>
</html>

msg1和msg3顯示的內容還是變換之前的,而msg2顯示的內容是變換之后的。其根本原因是因為Vue中DOM更新是異步的

 

 

需要注意的是,在 created 和 mounted 階段,如果需要操作渲染后的試圖,也要使用 nextTick 方法。

官方文檔說明:

注意 mounted 不會承諾所有的子組件也都一起被掛載。如果你希望等到整個視圖都渲染完畢,可以用 vm.$nextTick 替換掉 mounted
 
mounted: function () { this.$nextTick(function () { // Code that will run only after the // entire view has been rendered }) }


https://segmentfault.com/a/1190000012861862
思否參考↑


免責聲明!

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



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