【思考】$nextTick 與 setTimeout 的一點對比!

前端小智發表於2021-10-03
作者:Chimezie Enyinnaya
譯者:前端小智
來源:blog

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

一個前端開發人員(小智)走進了一個Vue酒吧。小智點了他最喜歡的雞尾酒:Nuxt。酒保正在努力製作中。然後他自己就嘮叨了起來。

小智講述了他是如何在Vue 3的例項方法下發現$nextTick的,並大吃一驚。小智
使用Vue已經有一段時間了,他已經習慣了把$watch$emit寫成例項方法。那麼,$nextTick是用來做什麼的?Vue文件說,它"[defers]回撥,在下一個DOM更新週期後執行"。

但是小智並不相信。

他繼續講述他是如何嘗試這樣做的:

this.loadingAnimation = true
this.startVeryLongCalculation()
this.completeVeryLongCalculation()
this.loadingAnimation = false

有用。 為什麼?

nextTick做什麼?

nextTick接受一個延遲到下一個DOM更新週期的回撥函式。這只是Vue的一種說法,"嘿,如果你想在DOM更新後執行一個函式(這種情況很少發生),我希望你使用nextTick而不是setTimeout"。

Vue.nextTick(() => {}) // syntax

下面很快就會講到setTimeoutnextTick引數。我們用這個例子來視覺化nextTick的行為:

<template>
  <div>
    {{ currentTime }}
  </div>
</template>

<script>
export default {
  name: 'getCurrentTime',
  data() {
    return {
      currentTime: ''
    }
  },
  mounted() {
    this.currentTime = 3;

    this.$nextTick(() => {
        let date = new Date()
        this.currentTime = date.getFullYear()
    });
  }
}
</script>

在J電腦上執行這個程式碼片段。它將顯示2021年。並不是說如果你去掉nextTick,就不會得到同樣的結果。然而,你應該明白,Vue會根據資料中的內容對DOM進行修改。

在上面的程式碼片段中,Vue將DOM更新為3,然後呼叫回撥,將DOM更新為2021,最後將控制權交給瀏覽器,瀏覽器將顯示2021

到目前為止,我們已經研究了nextTick在回撥佇列中插入回撥函式並在適當的時候執行該函式。

這個你可能會感興趣,nextTick中的回撥是作為事件迴圈中的一個微任務使用的。nextTick原始碼明確指出,"nextTick行為利用了微任務佇列,可以通過本地的Promise.thenMutationObserver來訪問。"

setTimeout vs nextTick

在DOM更新後執行函式的另一種方法是使用JavaScript的setTimeout()函式。

我們將上面的程式碼用setTimeout替換nextTick:

<template>
  <div>
    {{ currentTime }}
  </div>
</template>

<script>
export default {
  name: 'getCurrentTime',
  data() {
    return {
      currentTime: ''
    }
  },
  mounted() {
    this.currentTime = 3;

    setTimeout(() => {
      let date = new Date()
      this.currentTime = date.getFullYear()
    }, 0);
  }
}
</script>

執行此程式碼片段。 首先看到3然後2021。它發生得很快,因此如果沒有看到此行為,需要重新整理瀏覽器。

在上面的程式碼片段中,Vue將DOM更新為3,並提供瀏覽器控制。然後瀏覽器顯示3,呼叫回撥函式,將DOM更新到2021,最後將控制權交給瀏覽器,現在瀏覽器顯示2021

nextTick的實現在不支援PromiseMutationObserver的瀏覽器(IE 6-10和Opera Mini瀏覽器)上,使用setTimeout作為後備方法,對於不支援PromiseMutationObserver的瀏覽器(IE 10),它更傾向於setImmediate

何時使用 nexttick

  • 當你想使用setTimeout
  • 當你想確定DOM能反映你的資料時
  • 在嘗試執行非同步操作時,遇到Uncaught (in promise) DOMException等錯誤。記住,Vue是非同步更新DOM的

最後來個示例:

<div id="app">
  <div ref="listScroll" class="scrolledList">
    <ul ref="scrolledHeight">
      <li v-for="month in months">
        {{month}}
      </li>               
    </ul>
  </div>

  <input type="text" placeholder="Add Month" v-model="month">
  <button @click="addMessage" @keyup.enter="addMessage"> Add Month</button>
</div>

<script src="https://unpkg.com/vue@next"> 
  Vue.createApp({
    data() {
      return {
        month: '',
        months: ['Jan', 'Feb', 'Apr', 'May', 'June', 'July', 'Aug']
      }
    },
    mounted() {
      this.updateScrollNextTick()
    },
    methods: {
      addMessage() {
        if(this.month == ''){
          return
        }

        this.months.push(this.month)
        this.month = ''
        this.updateScrollNextTick()
      },
      updateScrollNextTick () {
        let scrolledHeight = this.$refs.scrolledHeight.clientHeight

        this.$nextTick(() => {
          this.$refs.listScroll.scrollTo({
            behavior: 'smooth',
            top: scrolledHeight
          })
        })
      }
    },
  })
  .mount("#app")
</script>

示例地址:https://codepen.io/ammezie/pe...

主要部分:

image.png

執行結果:

在上面的程式碼片斷中,我們想在一個新專案被新增到列表中時獲得平滑的向下滾動效果。瀏覽一下程式碼,嘗試修改一下,去掉nextTick,你就會失去那種平滑的滾動效果。你也可以嘗試用setTimeout來代替nextTick

總結

在本文中,我們探索了nextTick是如何工作的。我們進一步瞭解了它與普通的JavaScript setTimeout的不同之處,並介紹了實際的用例。

~完,我是小智,準備去教育一個前端小妹。

編輯中可能存在的bug沒法實時知道,事後為了解決這些bug,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

原文:https://blog.logrocket.com/un...

交流

文章每週持續更新,可以微信搜尋【大遷世界 】第一時間閱讀,回覆【福利】有多份前端視訊等著你,本文 GitHub https://github.com/qq449245884/xiaozhi 已經收錄,歡迎Star。

相關文章