[Vue] $nextTick()的理解和使用

先把我使用$nextTick()最多的場景放這里:
如果你在寫js/ts時要在更改完數(shù)據(jù)后,去操作DOM,那這個時候就要把操作DOM的代碼放到$nextTick()中。否則,會看到,調試的數(shù)據(jù)都是對的,但是頁面的行為怎么就是錯的。

直接查看Vue API文檔(https://vuejs.org/v2/api/#Vue-nextTick),看其對$nextTick()的定義:

image.png

這個定義的直接翻譯是:
“將回調延遲到下一個DOM更新周期之后執(zhí)行。在更改了一些數(shù)據(jù)以等待DOM更新之后立即使用它”。

作為從后端轉到前端的開發(fā),對前端的底層知識還沒深刻的理解, 第一次看到這句定義真的是摸不著頭腦:

  • 什么是DOM更新周期?
  • 為什么要延遲到‘下一個’DOM更新周期?
  • 為什么說是“下一個”?
  • 為什么要在更改了一些數(shù)據(jù)以等待DOM更新之后立即使用它?
  • 什么叫“以等待DOM更新之后“?
    總之是我看過最拗口的定義,無數(shù)個疑問都不知道從哪個開始突破。

那么現(xiàn)在是我第3次使用$nextTick(), 也是覺得理解對了$nextTick()的用法和目的,分享出來。

開始:
對于 $nextTick()的解釋,Vue的官方文檔放的比較零散,但是如果等通讀完文檔再著手開發(fā)項目又不現(xiàn)實。所以我摳出了截圖,我畫出 了重點,但建議你通讀截圖里的文字,這樣有助于整體理解:
其實$nextTick() API文檔下有個鏈接用于解釋“Vue的異步更新隊列”, 理解了這個就差不多理解了何時該用$nextTick() 。


https://vuejs.org/v2/api/#Vue-nextTick
https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue

翻譯過來是:Vue異步執(zhí)行DOM更新。每當觀察到數(shù)據(jù)更改時,它將打開一個隊列并緩沖同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)更改。如果同一個監(jiān)視程序被觸發(fā)多次,那么它將只被推入隊列一次。這種緩沖的重復數(shù)據(jù)刪除對于避免不必要的計算和DOM操作非常重要。然后,在下一個事件循環(huán)“tick”中,Vue刷新隊列并執(zhí)行實際的(已經被清除的)工作。在內部Vue嘗試原生承諾。然后,對異步隊列使用MutationObserver和setimmediation,并退回到setTimeout(fn, 0)。

這就解釋了前面的3個問題:
  • 什么是DOM更新周期?
  • 為什么要延遲到‘下一個’DOM更新周期?
  • 為什么說是“下一個”?
對于一個事件的數(shù)據(jù),從“數(shù)據(jù)更改”->"緩存入數(shù)據(jù)更新隊列"->在某個條件下,將這個緩存數(shù)據(jù)被更新到DOM(頁面出現(xiàn)更新)->這個事件再監(jiān)聽數(shù)據(jù)更改。這是一個周期。也就是說,你改了數(shù)據(jù),雖然Vue是響應式的更新,有些時候你看起來好像是立刻更新了,但其實不是,Vue把改動的數(shù)據(jù)先緩存起來了,要等下一個DOM更新周期到了才會把這些數(shù)據(jù)更新到DOM里,因此,如果說你只是“看”頁面,看不出區(qū)別。如果你要在更新完數(shù)據(jù)后,去操作DOM,那這個時候就會看到,調試的數(shù)據(jù)都是對的,但是頁面的行為怎么就是錯的。 這就是我的使用場景。

下面是一個應用舉例,括號里為測試數(shù)據(jù)。如果不想看,跳過直接看總結
這里,我要做到這樣的功能,刪除當前的filter(branch-test)后,頁面要顯示默認的filter(test4).
頁面的數(shù)據(jù)結構是這樣的:

  • myFilters:[]any=>所有的filter信息
  • activeFilter:any=>當前被選中的filter
    filter是Button, 點擊后顏色變化是通過getElementById()操作DOM 來實現(xiàn)的,正是因為這個,才需要用到$nextTick()

image.png

在下面代碼中,最后一個方法onFilterDelete 是實現(xiàn)這個功能的入口;
我說下沒有使用nextTick()看到的亂象,這樣你就能理解為什么要用nextTick():
最開始沒用$nextTick(),看到的亂象是:當我刪除了branch-test 這個filter后,橙色的字眼跳到了fontana, 而下面表格里羅列的卻是test4(default)的job, 也就是說,操作DOM得到的結果是錯的,而table數(shù)據(jù)是對的,說明數(shù)據(jù)沒有問題(當我在方法里打印出操作DOM的時候,它操作的ID也是對的)但是這里有我還沒想明白的疑問是,既然getElementById("test4")那此時舊的DOM里也有這個test4對象,為什么會渲染到fontana呢?知道的朋友也回復下

總結下:也就是說,如果你要在數(shù)據(jù)更改了之后,下一步是,需要手動(自己寫代碼)去更改DOM的內容(我把這個工作叫善后代碼段),那么,修改完數(shù)據(jù)后,善后代碼段包成一個方法,并把它放入

this.$nextTick(() => {
this.善后方法();
})

而且這3行代碼是跟在你修改數(shù)據(jù)的代碼后,像這樣,改完數(shù)據(jù)后要去修改DOM樣式:
        this.myFilters.splice(index, 1); //改數(shù)據(jù)
        console.log('After delete filter', this.myFilters)
        this.$nextTick(() => {      
            this.setDefaultFilter()  //修改DOM樣式
        })

以下是ts代碼:

    setDefaultFilter() {
        let defaultFilter = this.myFilters.find(f => f.isDefault);
        if (isEmpty(defaultFilter)) {
            defaultFilter = this.myFilters.find(f => f.name === 'All Jobs');
        }
        this.activeFilter = defaultFilter 
        console.log('[DEBUG] set color to orange for ', filter.name);
          // @ts-ignore
        document.getElementById(filter.name).style.color = "#e87532e0";
        for (let f of this.myFilters) {
            if (f.name !== filter.name) {
                // @ts-ignore
                document.getElementById(f.name).style.color = "dodgerblue"
            }
        }
//...
    }
    
 async onFilterDelete() {
       //...
        let res = await service.deleteFilter(this.activeFilter.id).toPromise();
        if (res.code !== 200) {
            errorHandler.handle("Fail to delete Filter: " + this.activeFilter.name)
            return;
        }
        let deleteFilterId = this.activeFilter.id;
        let index = findIndex(this.myFilters, function (o) {
            return ('id' in o) && o.id === deleteFilterId;
        });
        this.myFilters.splice(index, 1);
        console.log('After delete filter', this.myFilters)
        this.$nextTick(() => {      //重點代碼
            this.setDefaultFilter()  //重點代碼
        })
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容