欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue2和Vue3的nextTick實(shí)現(xiàn)原理

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

一次弄懂 Vue2 和 Vue3 的 nextTick 實(shí)現(xiàn)原理

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

Vue2 中的 nextTick

在 Vue2 中,nextTick 的實(shí)現(xiàn)基于瀏覽器的異步任務(wù)隊(duì)列和微任務(wù)隊(duì)列。

異步任務(wù)隊(duì)列

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

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

在 Vue2 中,如果沒有指定執(zhí)行環(huán)境,則會優(yōu)先使用 Promise.then / MutationObserver,否則使用 setTimeout。

javascript復(fù)制代碼
// 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
    })
  }
}

宏任務(wù)和微任務(wù)

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

Vue2 中默認(rèn)使用微任務(wù),在沒有原生 Promise 和 MutationObserver 的情況下,才會改用 setTimeout。

javascript復(fù)制代碼
let microTimerFunc
let macroTimerFunc
let useMacroTask = false // 默認(rèn)使用微任務(wù)

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
    })
  }
}

總結(jié)

在 Vue2 中,nextTick 的實(shí)現(xiàn)原理基于瀏覽器的異步任務(wù)隊(duì)列和微任務(wù)隊(duì)列。Vue2 默認(rèn)使用微任務(wù),在沒有原生 Promise 和 MutationObserver 的情況下才會改用 setTimeout。

Vue3 中的 nextTick

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

Promise 在瀏覽器中的問題

在瀏覽器中,Promise 有一個(gè)缺陷:如果 Promise 在當(dāng)前事件循環(huán)中被解決,那么在 then 回調(diào)函數(shù)之前添加的任務(wù)將不能在同一個(gè)任務(wù)中執(zhí)行。

例如:

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

console.log('Hello')

輸出結(jié)果為:

復(fù)制代碼
Hello
Promise 1
Promise 2

這是因?yàn)?Promise 雖然是微任務(wù),但是需要等到當(dāng)前宏任務(wù)結(jié)束才能執(zhí)行。

Vue3 中解決 Promise 缺陷的方法

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

javascript復(fù)制代碼
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 的實(shí)現(xiàn)原理基于MutationObserver 和 Promise.resolve().then(),通過 MutationObserver 監(jiān)測 DOM 變化,在下一個(gè)微任務(wù)中執(zhí)行回調(diào)函數(shù)。

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

如果需要等待所有回調(diào)函數(shù)執(zhí)行完成,則可以通過返回一個(gè) Promise 對象來實(shí)現(xiàn)。

javascript復(fù)制代碼
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()
      })
    })
  }
}

總結(jié)

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

結(jié)論

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

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

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

以上就是Vue2和Vue3的nextTick實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于Vue2和Vue3 nextTick實(shí)現(xiàn)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue自定義省市區(qū)三級聯(lián)動

    Vue自定義省市區(qū)三級聯(lián)動

    這篇文章主要為大家詳細(xì)介紹了Vue自定義省市區(qū)三級聯(lián)動,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • vue使用axios?post發(fā)送json數(shù)據(jù)跨域請求403的解決方案

    vue使用axios?post發(fā)送json數(shù)據(jù)跨域請求403的解決方案

    這篇文章主要介紹了vue使用axios?post發(fā)送json數(shù)據(jù)跨域請求403的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • element-ui中select組件綁定值改變,觸發(fā)change事件方法

    element-ui中select組件綁定值改變,觸發(fā)change事件方法

    今天小編就為大家分享一篇element-ui中select組件綁定值改變,觸發(fā)change事件方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • 使用 Vue 綁定單個(gè)或多個(gè) Class 名的實(shí)例代碼

    使用 Vue 綁定單個(gè)或多個(gè) Class 名的實(shí)例代碼

    這篇文章主要介紹了使用 Vue 綁定單個(gè)或多個(gè) Class 名的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧
    2018-01-01
  • vue移動端實(shí)現(xiàn)下拉刷新

    vue移動端實(shí)現(xiàn)下拉刷新

    我們通過原理講解以及代碼實(shí)例給大家分享了關(guān)于VUE實(shí)現(xiàn)移動下拉刷新的功能,有的朋友可以跟著學(xué)習(xí)下。
    2018-04-04
  • vue.js整合mint-ui里的輪播圖實(shí)例代碼

    vue.js整合mint-ui里的輪播圖實(shí)例代碼

    這篇文章主要介紹了vue.js整合mint-ui里的輪播圖的方法,首先我們需要初始化vue項(xiàng)目,然后安裝mint-ui。具體內(nèi)容詳情大家通過學(xué)習(xí)
    2017-12-12
  • 原生echart和vue-echart的使用詳解

    原生echart和vue-echart的使用詳解

    這篇文章主要為大家詳細(xì)介紹了原生echart和vue-echart的使用,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • 詳細(xì)聊聊Vue中的MVVM模式原理

    詳細(xì)聊聊Vue中的MVVM模式原理

    MVVM旨在利用WPF中的數(shù)據(jù)綁定函數(shù),通過從視圖層中幾乎刪除所有GUI代碼(代碼隱藏),更好地促進(jìn)視圖層開發(fā)與模式其余部分的分離,這篇文章主要給大家介紹了關(guān)于Vue.js中MVVM的相關(guān)資料,需要的朋友可以參考下
    2023-03-03
  • Vue + OpenLayers 快速入門學(xué)習(xí)教程

    Vue + OpenLayers 快速入門學(xué)習(xí)教程

    大家都知道使用 Openlayers可以很靈活自由的做出各種地圖和空間數(shù)據(jù)的展示。而且這個(gè)框架是完全免費(fèi)和開源的,本文記錄下 Vue 使用 OpenLayers 入門,使用 OpenLayers 創(chuàng)建地圖組件的相關(guān)知識,需要的朋友一起學(xué)習(xí)下吧
    2021-09-09
  • Vue使用axios發(fā)送請求簡單實(shí)現(xiàn)代碼

    Vue使用axios發(fā)送請求簡單實(shí)現(xiàn)代碼

    axios是一個(gè)基于Promise的,發(fā)送http請求的一個(gè)工具庫,并不是vue中的第三方插件,使用時(shí)不能通過“Vue.use()”安裝插件,需要在原型上進(jìn)行綁定
    2023-04-04

最新評論