最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue2和Vue3的nextTick實現原理

 更新時間:2023年04月27日 10:20:59   作者:前端mian  
Vue 中的數據綁定和模板渲染都是異步的,那么如何在更新完成后執(zhí)行回調函數呢?這就需要用到 Vue 的 nextTick 方法了,本文詳細介紹了Vue2和Vue3的nextTick實現原理,感興趣的同學可以參考一下

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

今天是 Wed Apr 26 2023 14:29:19 GMT+0800 (China Standard Time),我們來聊一下 Vue 的異步更新機制中的 nextTick。Vue 中的數據綁定和模板渲染都是異步的,那么如何在更新完成后執(zhí)行回調函數呢?這就需要用到 Vue 的 nextTick 方法了。

Vue2 中的 nextTick

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

異步任務隊列

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

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

在 Vue2 中,如果沒有指定執(zhí)行環(huán)境,則會優(yōu)先使用 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 在當前事件循環(huán)中被解決,那么在 then 回調函數之前添加的任務將不能在同一個任務中執(zhí)行。

例如:

javascript復制代碼
Promise.resolve().then(() => {
  console.log('Promise 1')
}).then(() => {
  console.log('Promise 2')
})

console.log('Hello')

輸出結果為:

復制代碼
Hello
Promise 1
Promise 2

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

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 監(jiān)測 DOM 變化,在下一個微任務中執(zhí)行回調函數。

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

如果需要等待所有回調函數執(zhí)行完成,則可以通過返回一個 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()
      })
    })
  }
}

總結

在 Vue3 中,nextTick 的實現原理基于 MutationObserver 和 Promise.resolve().then()。如果瀏覽器不支持原生 Promise,則使用 setTimeout 來模擬 Promise 的行為,并在回調函數執(zhí)行前添加一個空的定時器來強制推遲執(zhí)行。

結論

無論是在 Vue2 還是 Vue3 中,nextTick 都是用來處理 DOM 更新完畢后執(zhí)行回調函數的方法。在 Vue2 中,nextTick 的實現基于瀏覽器的異步任務隊列和微任務隊列,而在 Vue3 中,為了解決瀏覽器對 Promise 的缺陷和問題,使用 MutationObserver 和 Promise.resolve().then() 來實現。同時,Vue3 中的 nextTick 方法也支持返回 Promise 對象,方便等待所有回調函數執(zhí)行完成后再進行下一步操作。

需要注意的是,盡管 Vue3 中使用了 MutationObserver 和 Promise.resolve().then() 來解決 Promise 在瀏覽器中的缺陷,但在某些情況下(例如非激活標簽頁中),仍然可能會出現問題。因此,在實際使用中,還需要根據具體情況選擇合適的方案。

總之,了解 nextTick 的實現原理可以幫助我們更好地理解 Vue 中的異步更新機制,從而更好地優(yōu)化和調試應用程序。

以上就是Vue2和Vue3的nextTick實現原理的詳細內容,更多關于Vue2和Vue3 nextTick實現的資料請關注腳本之家其它相關文章!

相關文章

  • Vue如何引入遠程JS文件

    Vue如何引入遠程JS文件

    本篇文章主要介紹了Vue引入遠程JS文件,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • 談談我在vue-cli3中用預渲染遇到的坑

    談談我在vue-cli3中用預渲染遇到的坑

    這篇文章主要介紹了談談我在vue-cli3中用預渲染遇到的坑,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-04-04
  • Vue中禁止編輯的常見方法(以禁止編輯輸入框為例)

    Vue中禁止編輯的常見方法(以禁止編輯輸入框為例)

    在我們開發(fā)項目的時候,有時候我們不希望用戶對我們的頁面進行操作,尤其是輸入框之類的,這篇文章主要給大家介紹了Vue中禁止編輯的常見方法,文中介紹的方法主要以禁止編輯輸入框為例,需要的朋友可以參考下
    2024-02-02
  • Vue生命周期詳解

    Vue生命周期詳解

    這篇文章詳細介紹了Vue的生命周期,文中通過代碼示例介紹的非常詳細。對大家的學習有一定的參考借鑒價值,需要的朋友可以參考下
    2023-04-04
  • 解決vue-cli項目sourcemap因為文件重名導致的文件定位映射錯誤問題

    解決vue-cli項目sourcemap因為文件重名導致的文件定位映射錯誤問題

    這篇文章主要介紹了解決vue-cli項目sourcemap因為文件重名導致的文件定位映射錯誤問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • ant?design?vue?pro?支持多頁簽模式問題

    ant?design?vue?pro?支持多頁簽模式問題

    這篇文章主要介紹了ant?design?vue?pro?支持多頁簽模式問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • vue-resource攔截器設置頭信息的實例

    vue-resource攔截器設置頭信息的實例

    下面小編就為大家?guī)硪黄獀ue-resource攔截器設置頭信息的實例。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • 詳解在vue-cli中引用jQuery、bootstrap以及使用sass、less編寫css

    詳解在vue-cli中引用jQuery、bootstrap以及使用sass、less編寫css

    這篇文章主要介紹了詳解在vue-cli中引用jQuery、bootstrap以及使用sass、less編寫css,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Vue?dialog模態(tài)框的封裝方法

    Vue?dialog模態(tài)框的封裝方法

    這篇文章主要為大家詳細介紹了Vue?dialog模態(tài)框的封裝方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • vue中常用方法的用法匯總

    vue中常用方法的用法匯總

    Vue.js?是一個用于構建用戶界面的漸進式框架,本文主要為大家整理了一些常用的?Vue?方法及其詳細說明和代碼示例,有需要的小伙伴可以參考一下
    2023-11-11

最新評論

和顺县| 霞浦县| 施秉县| 特克斯县| 长垣县| 寿光市| 大英县| 石家庄市| 阳朔县| 乐都县| 湛江市| 靖边县| 古田县| 绥德县| 涞水县| 满城县| 宣武区| 竹北市| 锡林郭勒盟| 耒阳市| 东兰县| 若羌县| 阿拉善右旗| 高尔夫| 新竹县| 汕头市| 罗田县| 将乐县| 清徐县| 剑河县| 岳阳市| 靖宇县| 陕西省| 平乡县| 江北区| 扶沟县| 永丰县| 肃南| 宜兰市| 陆丰市| 弋阳县|