中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue2和Vue3的nextTick怎么實現

發布時間:2023-04-27 11:51:23 來源:億速云 閱讀:86 作者:iii 欄目:開發技術

這篇“Vue2和Vue3的nextTick怎么實現”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Vue2和Vue3的nextTick怎么實現”文章吧。

    一次弄懂 Vue2 和 Vue3 的 nextTick 實現原理

    Vue2 中的 nextTick

    在 Vue2 中,nextTick 的實現基于瀏覽器的異步任務隊列和微任務隊列。

    異步任務隊列

    在瀏覽器中,每個宏任務結束后會檢查微任務隊列,如果有任務則依次執行。當所有微任務執行完成后,才會執行下一個宏任務。因此可以通過將任務作為微任務添加到微任務隊列中,來確保任務在所有宏任務執行完畢后立即執行。

    而使用 setTimeout 可以將任務添加到異步任務隊列中,在下一輪事件循環中執行。

    在 Vue2 中,如果沒有指定執行環境,則會優先使用 Promise.then / MutationObserver,否則使用 setTimeout。

    javascript
    // src/core/util/next-tick.js
    
    /* istanbul ignore next */
    const callbacks = []
    let pending = false
    
    function flushCallbacks() {
      pending = false
      const copies = callbacks.slice(0)
      callbacks.length = 0
      for (let i = 0; i < copies.length; i++) {
        copies[i]()
      }
    }
    
    let microTimerFunc
    let macroTimerFunc
    let useMacroTask = false
    
    if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
      // 使用 setImmediate
      macroTimerFunc = () => {
        setImmediate(flushCallbacks)
      }
    } 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 {
      // 使用 setTimeout
      macroTimerFunc = () => {
        setTimeout(flushCallbacks, 0)
      }
    }
    
    if (typeof Promise !== 'undefined' && isNative(Promise)) {
      // 使用 Promise.then
      const p = Promise.resolve()
      microTimerFunc = () => {
        p.then(flushCallbacks)
      }
    } else {
      // 使用 MutationObserver
      const observer = new MutationObserver(flushCallbacks)
      const textNode = document.createTextNode(String(1))
      observer.observe(textNode, {
        characterData: true
      })
      microTimerFunc = () => {
        textNode.data = String(1)
      }
    }
    
    export function nextTick(cb?: Function, ctx?: Object) {
      let _resolve
      callbacks.push(() => {
        if (cb) {
          try {
            cb.call(ctx)
          } catch (e) {
            handleError(e, ctx, 'nextTick')
          }
        } else if (_resolve) {
          _resolve(ctx)
        }
      })
      if (!pending) {
        pending = true
        if (useMacroTask) {
          macroTimerFunc()
        } else {
          microTimerFunc()
        }
      }
      if (!cb && typeof Promise !== 'undefined') {
        return new Promise(resolve => {
          _resolve = resolve
        })
      }
    }

    宏任務和微任務

    在 Vue2 中,可以通過設置 useMacroTask 來使 nextTick 方法使用宏任務或者微任務。

    Vue2 中默認使用微任務,在沒有原生 Promise 和 MutationObserver 的情況下,才會改用 setTimeout。

    javascript
    let microTimerFunc
    let macroTimerFunc
    let useMacroTask = false // 默認使用微任務
    
    if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
      // 使用 setImmediate
      macroTimerFunc = () => {
        setImmediate(flushCallbacks)
      }
    } 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 {
      // 使用 setTimeout
      macroTimerFunc = () => {
        setTimeout(flushCallbacks, 0)
      }
    }
    
    if (typeof Promise !== 'undefined' && isNative(Promise)) {
      // 使用 Promise.then
      const p = Promise.resolve()
      microTimerFunc = () => {
        p.then(flushCallbacks)
      }
    } else {
      // 使用 MutationObserver
      const observer = new MutationObserver(flushCallbacks)
      const textNode = document.createTextNode(String(1))
      observer.observe(textNode, {
        characterData: true
      })
      microTimerFunc = () => {
        textNode.data = String(1)
      }
    }
    
    export function nextTick(cb?: Function, ctx?: Object) {
      let _resolve
      callbacks.push(() => {
        if (cb) {
          try {
            cb.call(ctx)
          } catch (e) {
            handleError(e, ctx, 'nextTick')
          }
        } else if (_resolve) {
          _resolve(ctx)
        }
      })
      if (!pending) {
        pending = true
        if (useMacroTask) {
          macroTimerFunc()
        } else {
          microTimerFunc()
        }
      }
      if (!cb && typeof Promise !== 'undefined') {
        return new Promise(resolve => {
          _resolve = resolve
        })
      }
    }

    總結

    在 Vue2 中,nextTick 的實現原理基于瀏覽器的異步任務隊列和微任務隊列。Vue2 默認使用微任務,在沒有原生 Promise 和 MutationObserver 的情況下才會改用 setTimeout。

    Vue3 中的 nextTick

    在 Vue3 中,nextTick 的實現有了較大變化,主要是為了解決瀏覽器對 Promise 的缺陷和問題。

    Promise 在瀏覽器中的問題

    在瀏覽器中,Promise 有一個缺陷:如果 Promise 在當前事件循環中被解決,那么在 then 回調函數之前添加的任務將不能在同一個任務中執行。

    例如:

    javascript
    Promise.resolve().then(() => {
      console.log('Promise 1')
    }).then(() => {
      console.log('Promise 2')
    })
    
    console.log('Hello')

    輸出結果為:

    Hello
    Promise 1
    Promise 2

    這是因為 Promise 雖然是微任務,但是需要等到當前宏任務結束才能執行。

    Vue3 中解決 Promise 缺陷的方法

    在 Vue3 中,通過使用 MutationObserver 和 Promise.resolve().then() 來解決 Promise 在瀏覽器中的缺陷。具體實現如下:

    javascript
    const queue: Array<Function> = []
    let has: { [key: number]: boolean } = {}
    let flushing = false
    let index = 0
    
    function resetSchedulerState() {
      queue.length = 0
      has = {}
      flushing = false
    }
    
    function flushSchedulerQueue() {
      flushing = true
      let job
      while ((job = queue.shift())) {
        if (!has[job.id]) {
          has[job.id] = true
          job()
        }
      }
      resetSchedulerState()
    }
    
    let macroTimerFunc
    let microTimerFunc
    
    if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
      macroTimerFunc = () => {
        setImmediate(flushSchedulerQueue)
      }
    } else {
      macroTimerFunc = () => {
        setTimeout(flushSchedulerQueue, 0)
      }
    }
    
    if (typeof Promise !== 'undefined' && isNative(Promise)) {
      const p = Promise.resolve()
      microTimerFunc = () => {
        p.then(flushSchedulerQueue)
        if (isIOS) setTimeout(noop)
      }
    } else {
      microTimerFunc = macroTimerFunc
    }
    
    export function nextTick(fn?: Function): Promise<void> {
      const id = index++
      const job = fn.bind(null)
      queue.push(job)
    
      if (!flushing) {
        if (useMacroTask) {
          macroTimerFunc()
        } else {
          microTimerFunc()
        }
      }
    
      if (!fn && typeof Promise !== 'undefined') {
        return new Promise(resolve => {
          resolvedPromise.then(() => {
            if (has[id] || !queue.includes(job)) {
              return
            }
            queue.splice(queue.indexOf(job), 1)
            resolve()
          })
        })
      }
    }

    在 Vue3 中,nextTick 的實現原理基于MutationObserver 和 Promise.resolve().then(),通過 MutationObserver 監測 DOM 變化,在下一個微任務中執行回調函數。

    而如果當前瀏覽器不支持原生 Promise,則使用 setTimeout 來模擬 Promise 的行為,并在回調函數執行前添加一個空的定時器來強制推遲執行(解決 iOS 中 setTimeout 在非激活標簽頁中的問題)。

    如果需要等待所有回調函數執行完成,則可以通過返回一個 Promise 對象來實現。

    javascript
    export function nextTick(fn?: Function): Promise<void> {
      const id = index++
      const job = fn.bind(null)
      queue.push(job)
    
      if (!flushing) {
        if (useMacroTask) {
          macroTimerFunc()
        } else {
          microTimerFunc()
        }
      }
    
      if (!fn && typeof Promise !== 'undefined') {
        return new Promise(resolve => {
          resolvedPromise.then(() => {
            if (has[id] || !queue.includes(job)) {
              return
            }
            queue.splice(queue.indexOf(job), 1)
            resolve()
          })
        })
      }
    }

    以上就是關于“Vue2和Vue3的nextTick怎么實現”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    麻城市| 五峰| 张北县| 龙南县| 芜湖市| 济阳县| 乌拉特中旗| 耿马| 上蔡县| 洞头县| 玛多县| 茶陵县| 司法| 仙桃市| 高尔夫| 哈尔滨市| 湟中县| 泽州县| 沾化县| 永福县| 谢通门县| 顺义区| 高邑县| 苏尼特右旗| 元谋县| 峨边| 和田市| 贵阳市| 渑池县| 法库县| 博湖县| 斗六市| 剑河县| 邹城市| 平潭县| 敦化市| 拜城县| 六安市| 永登县| 沅江市| 府谷县|