淺析 NextTick

Next-tick

NextTick 是作什麼到?vue

來自 Vue 官網講述: 在下次 DOM 更新循環結束以後執行延遲迴調。在修改數據以後當即使用這個方法,獲取更新後的 DOM。git

是否似曾類似github

Vue.nextTick(() => {})
this.$nextTick(() => {})
在 vue 中 created 函數鉤子函數執行的時候DOM 其實並未進行任何渲染,因此得放在 nextTick 中去獲取 dom,與其對於得生命週期鉤子函數是 mounted
BUT 中 Vue 中 for 渲染 dom 就算是中 mounted 調用 nextTick也不能獲取到具體到 dom,爲何
如今來一探究竟promise

先來看一下下面這個問題

打印的是什麼,請先思考【下面有答案】多線程

console.log(1)

setTimeout(() => {
  console.log(8)
}, 2000)

setTimeout(() => {
  console.log(3)
  Promise.resolve().then(() => {
    console.log(4)
  })
  setTimeout(() => {
    console.log(6)
  }, 3000)
}, 1000)

new Promise((resolve, reject) => {
  console.log(5)
  resolve()
}).then(() => {
  console.log(7)
})

console.log(2)
複製代碼

談 Event-Loop

js 是單線程執行,固然,如今又有了一個 worker 創造了多線程環境,可是 worker 受限不少, js 執行是有一個執行棧,主要分了,宏任務(macro-task)和 微任務(micro-task)dom

  • 宏任務有那些異步

    1. setTimeout
    2. I/O
    3. setInterval
    4. setImmediate
    5. 主線程
    6. MessageChannel
  • 微任務有那些函數

    1. Promise 系列 .then .catch .finally
    2. process.nexttick
    3. MutationObserver
// 執行流程
              (執行一個宏任務,產生宏任務,入棧)
                    ↑
--------↑------- 宏任務 ←--—--
        |           |         |
        |           |         |
        |           |         |
        |           ↓         | 沒有
        |         微任務 ------
        |           |↘
        |           |(執行全部微任務過程當中產生微任務,繼續執行)
        |           |
        |           | 有 執行完因此微任務
        |           |
        |___________↓

--------------------↓------------------
                直到棧爲空
複製代碼

咱們在來看上面的問題oop

  • 主線程 (宏任務) 打印 1 - 5 - 2 【new Promise 會當即執行,不屬於,微任務】
  • 執行全部微任務 promise.then 打印 7
  • 在執行棧中拋出一個【能夠執行的 -> 到時間,雖然,第一個 setTimeout 首先註冊,在任務隊列棧底】宏任務執行 3 - 4
  • 在執行全部微任務 【沒有】
  • 在拋出一個宏任務執行 8
  • 在執行全部微任務 【沒有】
  • 在拋出一個宏任務執行 6
  • 任務執行完畢
  • 1 5 2 7 3 4 8 6

在談 NextTick

我去.......,上面這一大堆和 NextTick 有什麼關係?
還真有點關係,看源碼,我只剪取了主要部分,源碼來自 wepypost

// 微任務
let microTimerFunc
// 宏任務
let macroTimerFunc
// 默認不用宏任務,由於微任務到優先級高於宏任務
let useMacroTask = false
// 判斷 setImmediate 能不能使用
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // 能使用註冊宏任務
  macroTimerFunc = () => {
    setImmediate(flushCallbacks)
  }
  // 是否支持 MessageChannel
} else if (typeof MessageChannel !== 'undefined' && (
  isNative(MessageChannel) ||
  // PhantomJS
  MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {
  // 經過命名通道來通訊
  const channel = new MessageChannel()
  const port = channel.port2
  channel.port1.onmessage = flushCallbacks
  macroTimerFunc = () => {
    port.postMessage(1)
  }
} else {
  macroTimerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}
// 判斷是否可使用 Promise ie 8 如下不能吧.....
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  // 註冊微任務
  microTimerFunc = () => {
    p.then(flushCallbacks)
  }
} else {
  // 若是不存在,微任務註冊宏任務
  microTimerFunc = macroTimerFunc
}

/** 複製代碼

執行流程就是
宏任務 檢測 setImmediate ----- 不能 ----> 降級 MessageChannel ------不能-----> 降級 setTimeout
微任務 Promise ---- 不能 ---> 微任務註冊微宏任務
上面的 flushCallbacks 就是你要執行的函數

在 Vue 整個 nextTick 的做用

// 盜用官方的一個例子
  Vue.component('example', {
  template: '<span>{{ message }}</span>',
  data: function () {
    return {
      message: '未更新'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => '已更新'
      })
    }
  }
})
// 在 updateMessage 方法中,更新數據,當即獲取更新後的 dom 是獲取不到的,因此得把獲取 dom 加到事件隊列的棧,異步獲取更新後的dom 
複製代碼

主線程更新前 ---> 遇到宏任務或微任務 ---> 放入棧 ---> 主線程執行完成,更新完成 ----> 執行棧 ---- > 獲取更新後的dom

總結

相關文章
相關標籤/搜索