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

深入探究Vue中$nextTick的實(shí)現(xiàn)原理

 更新時(shí)間:2023年06月21日 10:51:13   作者:雅伊  
這篇文章主要為大家詳細(xì)介紹Vue中$nextTick的實(shí)現(xiàn)原理,文中的示例代碼講解詳細(xì),對(duì)我們深入了解Vue有一定的幫助,需要的小伙伴可以參考一下

前言

Vue 實(shí)現(xiàn)響應(yīng)式并不是數(shù)據(jù)發(fā)生變化之后 DOM 立即變化,而是按一定的策略進(jìn)行 DOM 的更新。

Vue在修改數(shù)據(jù)后,視圖不會(huì)立刻更新,而是等同一事件循環(huán)中的所有數(shù)據(jù)變化完成之后,再統(tǒng)一進(jìn)行視圖更新。

所以如果你用一個(gè)for循環(huán)來(lái)動(dòng)態(tài)改變數(shù)據(jù)100次,實(shí)際上它只會(huì)應(yīng)用最后一次改變,如果沒(méi)有這種機(jī)制,DOM就進(jìn)行 100 次的重繪,這固然是一個(gè)很大的開(kāi)銷。

而這里就涉及 Vue 一個(gè)重要的概念:異步更新隊(duì)列。

如果想要在修改數(shù)據(jù)之后立即獲取更新后的 DOM 可以使用 $nextTick。

今天就來(lái)看看 $nextTick 是如何實(shí)現(xiàn)的吧?。?!

JS 運(yùn)行機(jī)制 (Event Loop)

JS 執(zhí)行是單線程的,它是基于事件循環(huán)的。事件循環(huán)大致分為以下幾個(gè)步驟:

  • 所有同步任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧(execution context stack)。
  • 主線程之外,還存在一個(gè)"任務(wù)隊(duì)列"(task queue)。只要異步任務(wù)有了運(yùn)行結(jié)果,就在"任務(wù)隊(duì)列"之中放置一個(gè)事件。
  • 一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)讀取"任務(wù)隊(duì)列",看看里面有哪些事件。那些對(duì)應(yīng)的異步任務(wù),于是結(jié)束等待狀態(tài),進(jìn)入執(zhí)行棧,開(kāi)始執(zhí)行。
  • 主線程不斷重復(fù)上面的第三步。

這里主線程的執(zhí)行過(guò)程就是一個(gè)tick,而所有的異步結(jié)果都是通過(guò)任務(wù)隊(duì)列來(lái)調(diào)度。

Loop 分為宏任務(wù)和微任務(wù),無(wú)論是執(zhí)行宏任務(wù)還是微任務(wù),完成后都會(huì)進(jìn)入到一下tick,并在兩個(gè)tick之間進(jìn)行UI渲染。

由于Vue DOM更新是異步執(zhí)行的,即修改數(shù)據(jù)時(shí),視圖不會(huì)立即更新,而是會(huì)監(jiān)聽(tīng)數(shù)據(jù)變化,并緩存在同一事件循環(huán)中,等同一數(shù)據(jù)循環(huán)中的所有數(shù)據(jù)變化完成之后,再統(tǒng)一進(jìn)行視圖更新。為了確保得到更新后的DOM,所以設(shè)置了 Vue.nextTick()方法。

異步執(zhí)行的任務(wù)又分為宏任務(wù)、微任務(wù)

$nextTick

是Vue的核心方法之一,官方文檔解釋如下:

在下次DOM更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)。在修改數(shù)據(jù)之后立即使用這個(gè)方法,獲取更新后的DOM。

nextTick 原理

Vue是異步執(zhí)行dom更新的,一旦觀察到數(shù)據(jù)變化,Vue就會(huì)開(kāi)啟一個(gè)隊(duì)列,然后把在同一個(gè)事件循環(huán) (event loop) 當(dāng)中觀察到數(shù)據(jù)變化的 watcher 推送進(jìn)這個(gè)隊(duì)列。如果這個(gè)watcher被觸發(fā)多次,只會(huì)被推送到隊(duì)列一次。這種緩沖行為可以有效的去掉重復(fù)數(shù)據(jù)造成的不必要的計(jì)算和Dom操作。而在下一個(gè)事件循環(huán)時(shí),Vue會(huì)清空隊(duì)列,并進(jìn)行必要的DOM更新。

當(dāng)你設(shè)置 vm.someData = ‘new value’,DOM 并不會(huì)馬上更新,而是在異步隊(duì)列被清除,也就是下一個(gè)事件循環(huán)開(kāi)始時(shí)執(zhí)行更新時(shí)才會(huì)進(jìn)行必要的DOM更新。如果此時(shí)你想要根據(jù)更新的 DOM 狀態(tài)去做某些事情,就會(huì)出現(xiàn)問(wèn)題。為了在數(shù)據(jù)變化之后等待 Vue 完成更新 DOM ,可以在數(shù)據(jù)變化之后立即使用 Vue.nextTick(callback) 。這樣回調(diào)函數(shù)在 DOM 更新完成后就會(huì)調(diào)用。

源碼淺析

nextTick 的實(shí)現(xiàn)單獨(dú)有一個(gè)JS文件來(lái)維護(hù)它,在src/core/util/next-tick.js中。

nextTick 源碼主要分為兩塊:能力檢測(cè)和根據(jù)能力檢測(cè)以不同方式執(zhí)行回調(diào)隊(duì)列。

watcher 觸發(fā) nextTick

當(dāng)數(shù)據(jù)發(fā)生變化時(shí) dep 通知 watcher 執(zhí)行 update 更新

/**
 * 根據(jù) watcher 配置項(xiàng),決定接下來(lái)怎么走,一般是 queueWatcher
 */
update () {
  /* istanbul ignore else */
  if (this.lazy) {
    // 懶執(zhí)行時(shí)走這里,比如 computed
    // 將 dirty 置為 true,可以讓 computedGetter 執(zhí)行時(shí)重新計(jì)算 computed 回調(diào)函數(shù)的執(zhí)行結(jié)果
    this.dirty = true
  } else if (this.sync) {
    // 同步執(zhí)行,在使用 vm.$watch 或者 watch 選項(xiàng)時(shí)可以傳一個(gè) sync 選項(xiàng),
    // 當(dāng)為 true 時(shí)在數(shù)據(jù)更新時(shí)該 watcher 就不走異步更新隊(duì)列,直接執(zhí)行 this.run 
    // 方法進(jìn)行更新
    // 這個(gè)屬性在官方文檔中沒(méi)有出現(xiàn)
    this.run()
  } else {
    // 更新時(shí)一般都這里,將 watcher 放入 watcher 隊(duì)列
    queueWatcher(this)
  }
}

queueWatcher

/**
 * 將 watcher 放入 watcher 隊(duì)列 
 */
export function queueWatcher (watcher: Watcher) {
  const id = watcher.id
  // 如果 watcher 已經(jīng)存在,則跳過(guò),不會(huì)重復(fù)入隊(duì)
  if (has[id] == null) {
    // 緩存 watcher.id,用于判斷 watcher 是否已經(jīng)入隊(duì)
    has[id] = true
    if (!flushing) {
      // 當(dāng)前沒(méi)有處于刷新隊(duì)列狀態(tài),watcher 直接入隊(duì)
      queue.push(watcher)
    } else {
      // 已經(jīng)在刷新隊(duì)列了
      // 從隊(duì)列末尾開(kāi)始倒序遍歷,根據(jù)當(dāng)前 watcher.id 找到它大于的 watcher.id 的位置,然后將自己插入到該位置之后的下一個(gè)位置
      // 即將當(dāng)前 watcher 放入已排序的隊(duì)列中,且隊(duì)列仍是有序的
      let i = queue.length - 1
      while (i > index && queue[i].id > watcher.id) {
        i--
      }
      queue.splice(i + 1, 0, watcher)
    }
    // queue the flush
    if (!waiting) {
      // waiting = false 表示當(dāng)前瀏覽器的異步任務(wù)隊(duì)列沒(méi)有 flushSchedulerQueue 
      waiting = true
      if (process.env.NODE_ENV !== 'production' && !config.async) {
        // 直接去刷新 watcher 調(diào)度隊(duì)列 
        // 一般不會(huì)走這兒,Vue 默認(rèn)是異步執(zhí)行,如果改為同步執(zhí)行,性能會(huì)大打折扣
        flushSchedulerQueue()
        return
      }
      /**
       *   熟悉的 nextTick => vm.$nextTick、Vue.nextTick
       *   1、將 回調(diào)函數(shù)(flushSchedulerQueue) 放入 callbacks 數(shù)組
       *   2、通過(guò) pending 控制向?yàn)g覽器任務(wù)隊(duì)列中添加 flushCallbacks 函數(shù)
       */
      nextTick(flushSchedulerQueue)
    }
  }
}

watcher 更新時(shí) watcher 依次入隊(duì),避免 watcher 重復(fù)入隊(duì)

$nextTick 本質(zhì)是 promise

export function nextTick(cb? Function, ctx: Object) {
    let _resolve
    // cb 即為傳入的 flushSchedulerQueue函數(shù),會(huì)統(tǒng)一處理壓入callbacks數(shù)組
    // callbacks也就是異步操作隊(duì)列
    callbacks.push(() => {
        if(cb) {
            try {
                cb.call(ctx)  // 調(diào)用 flushSchedulerQueue 函數(shù),將其放入 callbacks異步隊(duì)列
            } catch(e) {
                handleError(e, ctx, 'nextTick')
            }
        } else if (_resolve) {
            _resolve(ctx)
        }
    })
    // pending 為false 說(shuō)明本輪事件循環(huán)中沒(méi)有執(zhí)行過(guò)timerFunc()
    if(!pending) {
        pending = true
        timerFunc()
    }
    // 當(dāng)不傳入 cb 參數(shù)時(shí),提供一個(gè)promise化的調(diào)用 
    if(!cb && typeof Promise !== 'undefined') {
      // 如nextTick().then(() => {})
      // 當(dāng)_resolve執(zhí)行時(shí),就會(huì)跳轉(zhuǎn)到then邏輯中
        return new Promise(resolve => {
            _resolve = resolve
        })
    }
}

next-tick.js 對(duì)外暴露了 nextTick 這個(gè)參數(shù),所以每次調(diào)用 Vue.nextTick 時(shí):

會(huì)把傳入的回調(diào)函數(shù) cb 壓入 callbacks 數(shù)組執(zhí)行 timerFunc 函數(shù),延遲調(diào)用 flushCallbacks 函數(shù)遍歷執(zhí)行 callbacks 數(shù)組中的所有函數(shù)

這里的 callbacks 沒(méi)有直接在 nextTick 中執(zhí)行回調(diào)函數(shù)的原因是保證在同一個(gè) tick 內(nèi)多次執(zhí)行nextTick,不會(huì)開(kāi)啟多個(gè)異步任務(wù),而是把這些異步任務(wù)都?jí)撼梢粋€(gè)同步任務(wù),在下一個(gè) tick 執(zhí)行完畢。

MutationObserver

先簡(jiǎn)單介紹下 MutationObserver:MO 是 HTML5 中的 API,用于監(jiān)視 DOM 變動(dòng)的接口,它可以用于監(jiān)控任何 DOM 節(jié)點(diǎn)的變更如子節(jié)點(diǎn)的刪除、屬性的修改、文本內(nèi)容修改等。

調(diào)用過(guò)程是要先給它綁定回調(diào),得到 MO 實(shí)例,這個(gè)回調(diào)會(huì)在 MO 實(shí)例監(jiān)聽(tīng)到變動(dòng)時(shí)觸發(fā)。 這里 MO 的回調(diào)是放在microtask中執(zhí)行的。

// 創(chuàng)建MO實(shí)例
const observer = new MutationObserver(callback)
const textNode = '想要監(jiān)聽(tīng)的Don節(jié)點(diǎn)'
observer.observe(textNode, {
  characterData: true // 說(shuō)明監(jiān)聽(tīng)文本內(nèi)容的修改
})

timerFunc

由于宏任務(wù)耗費(fèi)的時(shí)間是大于微任務(wù)的,所以在瀏覽器支持的情況下,優(yōu)先使用微任務(wù)。如果瀏覽器不支持微任務(wù),再使用宏任務(wù)。

源碼路徑位于:src\core\util\next-tick.js

/**
 * flushCallbacks 做了三件事:
 *   1、將 pending 置為 false  表示下一個(gè) flushCallbacks() 可以進(jìn)入瀏覽器異步任務(wù)隊(duì)列
 *   2、清空 callbacks 數(shù)組
 *   3、執(zhí)行 callbacks 數(shù)組中的所有函數(shù)(比如 flushSchedulerQueue、用戶調(diào)用 nextTick 傳遞的回調(diào)函數(shù))
 */
// 對(duì)callbacks進(jìn)行遍歷,然后執(zhí)行相應(yīng)的回調(diào)函數(shù)
function flushCallbacks () {
    pending = false
    // 這里拷貝的原因是:
    // 有的cb 執(zhí)行過(guò)程中又會(huì)往 callbacks 中加入內(nèi)容
    // 比如 $nextTick的回調(diào)函數(shù)里還有$nextTick
    // 后者的應(yīng)該放到下一輪的nextTick 中執(zhí)行
    // 所以拷貝一份當(dāng)前的,遍歷執(zhí)行完當(dāng)前的即可,避免無(wú)休止的執(zhí)行下去
    const copies = callbcks.slice(0)
    callbacks.length = 0 // callbacks里面的函數(shù)
    for(let i = 0; i < copies.length; i++) {
        copies[i]() // 執(zhí)行 callbacks 數(shù)組中的每一個(gè)函數(shù)
    }
}
let timerFunc // 異步執(zhí)行函數(shù) 用于異步延遲調(diào)用 flushCallbacks 函數(shù)
// 優(yōu)先使用 Promise  首選 Promise.resolve().then()
if(typeof Promise !== 'undefined' && isNative(Promise)) {
    const p = Promise.resolve()
   // 首選 Promise.resolve().then()
    timerFunc = () => {
        // 在 微任務(wù)隊(duì)列 中放入 flushCallbacks 函數(shù) 
        p.then(flushCallbacks)
        // IOS 的UIWebView, Promise.then 回調(diào)被推入 microTask 隊(duì)列,但是隊(duì)列可能不會(huì)如期執(zhí)行
        // 因此,添加一個(gè)空計(jì)時(shí)器強(qiáng)制執(zhí)行 microTask
        if(isIOS) setTimeout(noop)
    }
    isUsingMicroTask = true
} else if(!isIE && typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) || MutationObserver.toString === '[object MutationObserverConstructor]')) {
    // 當(dāng) 原生 Promise 不可用時(shí),使用 原生 MutationObserver
    let counter = 1
    // 創(chuàng)建MO實(shí)例,監(jiān)聽(tīng)到DOM變動(dòng)后會(huì)執(zhí)行回調(diào)flushCallbacks
    const observer = new MutationObserver(flushCallbacks)
    const textNode = document.createTextNode(String(counter))
    observer.observe(textNode, {
        characterData: true // 設(shè)置true 表示觀察目標(biāo)的改變
    })
    // 每次執(zhí)行timerFunc 都會(huì)讓文本節(jié)點(diǎn)的內(nèi)容在 0/1之間切換
    // 切換之后將新值復(fù)制到 MO 觀測(cè)的文本節(jié)點(diǎn)上
    // 節(jié)點(diǎn)內(nèi)容變化會(huì)觸發(fā)回調(diào)
    timerFunc = () => {
        counter = (counter + 1) % 2
        textNode.data = String(counter) // 觸發(fā)回調(diào)
    }
    isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
   // setImmediate 宏任務(wù)隊(duì)列,  setImmediate 性能優(yōu)于 setTimeout 將flushCallbacks 放入瀏覽器的異步任務(wù)隊(duì)列
    timerFunc = () => {
        setImmediate(flushCallbacks)
    }
} else {
    timerFunc = () => {
        setTimeout(flushCallbacks, 0)
    }
}

關(guān)鍵在于timeFunc(),該函數(shù)起到延遲執(zhí)行的作用。

nextTick 的目的就是產(chǎn)生一個(gè)回調(diào)函數(shù)加入 task 或者 microtask 中,當(dāng)前棧執(zhí)行完以后(可能中間還有別的排在前面的函數(shù))調(diào)用該回調(diào)函數(shù),起到了異步觸發(fā)(即下一個(gè)tick時(shí)觸發(fā))的目的。

timeFunc() 有四種實(shí)現(xiàn):

1.promise:

如果瀏覽器支持Promise,那么就用 Promise.then 的方式來(lái)延遲函數(shù)調(diào)用,Promise.then() 可以將函數(shù)延遲到當(dāng)前函數(shù)調(diào)用棧最末端,也就是函數(shù)調(diào)用棧最后調(diào)用該函數(shù)。從而做到延遲

2.MutationObserver

MutationObserver 是H5 新加的一個(gè)功能,其功能是監(jiān)聽(tīng) DOM 節(jié)點(diǎn)的變動(dòng),在所有 DOM 變動(dòng)完成后,執(zhí)行回調(diào)函數(shù)。

3.setImmediate、setTimeout

利用 setImmediate、setTimeout 的延遲原理,setTimeout(func, 0)會(huì)將func函數(shù)延遲到下一次函數(shù)調(diào)用棧的開(kāi)始,也就是當(dāng)前函數(shù)執(zhí)行完畢后再執(zhí)行該函數(shù),因此完成了延遲功能。

**而 setImmediate 性能優(yōu)于 setTimeout **

延遲調(diào)用優(yōu)先級(jí)如下:

Promise.then > MutationObserver > setImmediate > setTimeout

為什么優(yōu)先使用 microtack

JS 的 event loop 執(zhí)行時(shí)會(huì)區(qū)分 task 和 microtask,引擎在每個(gè) task 執(zhí)行完畢,從隊(duì)列中取下一個(gè) task 來(lái)執(zhí)行之前,會(huì)先執(zhí)行完所有 microtask 隊(duì)列中的 microtask。

setTimeout 回調(diào)會(huì)被分配到一個(gè)新的 task 中執(zhí)行,而 Promise 的 resolver、MutationObserver 的回調(diào)都會(huì)被安排到一個(gè)新的 microtask 中執(zhí)行,會(huì)比 setTimeout 產(chǎn)生的 task 先執(zhí)行。

要?jiǎng)?chuàng)建一個(gè)新的 microtask,優(yōu)先使用 Promise,如果瀏覽器不支持,再嘗試 MutationObserver。

實(shí)在不行,只能用 setTimeout 創(chuàng)建 task 了。

為啥要用 microtask? 根據(jù) HTML Standard,在每個(gè) task 運(yùn)行完以后,UI 都會(huì)重渲染,那么在 microtask 中就完成數(shù)據(jù)更新,當(dāng)前 task 結(jié)束就可以得到最新的 UI 了。

反之如果新建一個(gè) task 來(lái)做數(shù)據(jù)更新,那么渲染就會(huì)進(jìn)行兩次。

flushCallbacks

依次執(zhí)行callbacks中的函數(shù)

/**
 * 做了三件事:
 *   1、將 pending 置為 false  表示下一個(gè) flushCallbacks() 可以進(jìn)入瀏覽器異步任務(wù)隊(duì)列
 *   2、清空 callbacks 數(shù)組
 *   3、執(zhí)行 callbacks 數(shù)組中的所有函數(shù)(比如 flushSchedulerQueue、用戶調(diào)用 nextTick 傳遞的回調(diào)函數(shù))
 */
function flushCallbacks () {
  // 微任務(wù)隊(duì)列中只能有一個(gè) flushCallbacks()
  pending = false
  const copies = callbacks.slice(0) // 清空 callbacks 數(shù)組
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

callbacks 異步隊(duì)列中存放了 flushSchedulerQueue 函數(shù),

在 flashcallbacks() 刷新隊(duì)列的過(guò)程中依次執(zhí)行 callbacks 數(shù)組中的 flushSchedulerQueue 函數(shù)

flushSchedulerQueue

刷新隊(duì)列,保證了 watcher 的父子關(guān)系順序,依次執(zhí)行 watcher 的 run 方法

function flushSchedulerQueue () {
  currentFlushTimestamp = getNow()
  //  flushing = true 表示 watcher 隊(duì)列正在被刷新
  flushing = true
  let watcher, id
  // 確保 watcher 的先后順序
  // watcher 隊(duì)列排序  watcher 的id 由小到大有序遞增  保證組件更新順序  
  // 父組件的 watcher 先于子組件的 watcher 更新
  // 用戶 watcher 先于 渲染 watcher 更新
  // 父組件的 watcher 在執(zhí)行時(shí) 子組件的 watcher 銷毀 可以跳過(guò)子組件的執(zhí)行 
  queue.sort((a, b) => a.id - b.id)
  //  for 循環(huán)遍歷 watcher 隊(duì)列  依次執(zhí)行 watcher 的 run 方法
  // queue.length 動(dòng)態(tài)計(jì)算 確保是最新的隊(duì)列
  for (index = 0; index < queue.length; index++) {
    // 拿出當(dāng)前索引的 watcher
    watcher = queue[index]
    // 首先執(zhí)行 brfore 鉤子  執(zhí)行beforeUpdate()
    if (watcher.before) {
      watcher.before() // watcher 實(shí)例化時(shí) 傳入的 before 方法
    }
    // 清空緩存 表示當(dāng)前 watcher 已經(jīng)被執(zhí)行  當(dāng)該 watcher 再次入隊(duì)時(shí)可以進(jìn)入
    id = watcher.id
    has[id] = null
    // 執(zhí)行 watcher 的run() 
    watcher.run()
    if (process.env.NODE_ENV !== 'production' && has[id] != null) {
      circular[id] = (circular[id] || 0) + 1
      if (circular[id] > MAX_UPDATE_COUNT) {
        warn(
          'You may have an infinite update loop ' + (
            watcher.user
              ? `in watcher with expression "${watcher.expression}"`
              : `in a component render function.`
          ),
          watcher.vm
        )
        break
      }
    }
  }
  // keep copies of post queues before resetting state
  const activatedQueue = activatedChildren.slice()
  const updatedQueue = queue.slice()
  resetSchedulerState()
  // call component updated and activated hooks
  callActivatedHooks(activatedQueue)
  callUpdatedHooks(updatedQueue)
  // devtool hook
  /* istanbul ignore if */
  if (devtools && config.devtools) {
    devtools.emit('flush')
  }
}

flushSchedulerQueue() 主要負(fù)責(zé)依次執(zhí)行 queue 數(shù)組中每個(gè) watcher 的 run 方法,從而進(jìn)入更新階段。

刷新隊(duì)列,由 flushCallbacks 函數(shù)負(fù)責(zé)調(diào)用,主要做了如下兩件事:

  • 更新 flushing 為 ture,表示正在刷新隊(duì)列,在此期間往隊(duì)列中 push 新的 watcher 時(shí)需要特殊處理(將其放在隊(duì)列的合適位置)
  • 按照隊(duì)列中的 watcher.id 從小到大排序,保證先創(chuàng)建的 watcher 先執(zhí)行,也配合 第一步
  • 遍歷 watcher 隊(duì)列,依次執(zhí)行 watcher.before、watcher.run,并清除緩存的 watcher

$nextTick 的應(yīng)用場(chǎng)景

1.在Vue生命周期的created()鉤子函數(shù)進(jìn)行的DOM操作一定要放在nextTick的回調(diào)函數(shù)中。

原因是created()鉤子函數(shù)執(zhí)行時(shí)DOM其實(shí)并未進(jìn)行渲染。

2.在數(shù)據(jù)變化后要執(zhí)行的某個(gè)操作,而這個(gè)操作需要使用隨數(shù)據(jù)改變而改變的 DOM 結(jié)構(gòu)的時(shí)候,這個(gè)操作應(yīng)該放在nextTick()的回調(diào)函數(shù)中。

因?yàn)椋篤ue異步執(zhí)行DOM更新,只要觀察到數(shù)據(jù)變化,Vue將開(kāi)啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)改變,如果同一個(gè) watcher 被多次觸發(fā),只會(huì)被推入到隊(duì)列中一次。

總結(jié)

使用nextTick()是為了可以獲取更新后的DOM 。

觸發(fā)時(shí)機(jī):在同一事件循環(huán)中的數(shù)據(jù)變化后,DOM完成更新,立即執(zhí)行nextTick()的回調(diào)。

同一事件循環(huán)中的代碼執(zhí)行完畢 -> DOM 更新 -> nextTick callback觸發(fā)

Vue 的 nextTick 是如何實(shí)現(xiàn)

  • 將傳遞的回調(diào)函數(shù)放入 callbacks 全局?jǐn)?shù)組中
  • 調(diào)用 timerFunc 函數(shù),在瀏覽器的異步隊(duì)列中放入刷新callbacks(flashcallbacks ) 函數(shù) ,延遲執(zhí)行 (根據(jù)運(yùn)行環(huán)境判斷將flashcallbacks() 放入宏任務(wù)或者是微任務(wù)隊(duì)列, 使得 flashcallbacks 被延遲調(diào)用)
  • 事件循環(huán)到了微任務(wù)或者宏任務(wù),依次遍歷執(zhí)行 callbacks 數(shù)組中的所有函數(shù)

到此這篇關(guān)于深入探究Vue中$nextTick的實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Vue $nextTick內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 在vue中使用Base64轉(zhuǎn)碼的案例

    在vue中使用Base64轉(zhuǎn)碼的案例

    這篇文章主要介紹了在vue中使用Base64轉(zhuǎn)碼的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-08-08
  • VueJS事件處理器v-on的使用方法

    VueJS事件處理器v-on的使用方法

    本篇文章主要介紹了VueJS事件處理器v-on的使用方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • element?ui動(dòng)態(tài)側(cè)邊菜單欄及頁(yè)面布局實(shí)現(xiàn)方法

    element?ui動(dòng)態(tài)側(cè)邊菜單欄及頁(yè)面布局實(shí)現(xiàn)方法

    后臺(tái)管理系統(tǒng)經(jīng)常會(huì)使用到一個(gè)左側(cè)菜單欄,右側(cè)Tab頁(yè)的頁(yè)面顯示結(jié)構(gòu),這篇文章主要給大家介紹了關(guān)于element?ui動(dòng)態(tài)側(cè)邊菜單欄及頁(yè)面布局實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2023-09-09
  • 淺析vue cli3 封裝Svgicon組件正確姿勢(shì)(推薦)

    淺析vue cli3 封裝Svgicon組件正確姿勢(shì)(推薦)

    這篇文章主要介紹了vue cli3 封裝Svgicon組件正確姿勢(shì),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Vue使用vue-draggable 插件在不同列表之間拖拽功能

    Vue使用vue-draggable 插件在不同列表之間拖拽功能

    這篇文章主要介紹了使用vue-draggable 插件在不同列表之間拖拽,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03
  • elementPuls 表格反選實(shí)現(xiàn)示例代碼

    elementPuls 表格反選實(shí)現(xiàn)示例代碼

    這篇文章主要介紹了elementPuls 表格反選實(shí)現(xiàn)示例代碼,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-07-07
  • Vue.js教程之a(chǎn)xios與網(wǎng)絡(luò)傳輸?shù)膶W(xué)習(xí)實(shí)踐

    Vue.js教程之a(chǎn)xios與網(wǎng)絡(luò)傳輸?shù)膶W(xué)習(xí)實(shí)踐

    這篇文章主要給大家介紹了Vue.js之a(chǎn)xios與網(wǎng)絡(luò)傳輸?shù)南嚓P(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟隨小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。
    2017-04-04
  • 解決element-ui的下拉框有值卻無(wú)法選中的情況

    解決element-ui的下拉框有值卻無(wú)法選中的情況

    這篇文章主要介紹了解決element-ui的下拉框有值卻無(wú)法選中的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • vue-cli創(chuàng)建的項(xiàng)目中的gitHooks原理解析

    vue-cli創(chuàng)建的項(xiàng)目中的gitHooks原理解析

    這篇文章主要介紹了vue-cli創(chuàng)建的項(xiàng)目中的gitHooks原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • element-ui?form表單的動(dòng)態(tài)rules校驗(yàn)功能實(shí)現(xiàn)

    element-ui?form表單的動(dòng)態(tài)rules校驗(yàn)功能實(shí)現(xiàn)

    在vue項(xiàng)目中,有時(shí)候可能會(huì)用到element-ui?form表單的動(dòng)態(tài)rules校驗(yàn),這篇文章主要介紹了element-ui form表單的動(dòng)態(tài)rules校驗(yàn),我們可以巧妙的運(yùn)用element-ui form表單里面form-item的校驗(yàn)規(guī)則來(lái)處理,本文結(jié)合實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07

最新評(píng)論

内黄县| 肇东市| 井陉县| 古浪县| 古交市| 耿马| 福泉市| 肇庆市| 游戏| 鄂托克前旗| 江津市| 水富县| 嘉义市| 如皋市| 凉城县| 砀山县| 沂水县| 林周县| 神池县| 会同县| 安达市| 大庆市| 汤阴县| 张北县| 乐安县| 晋江市| 清涧县| 福鼎市| 嵊泗县| 米林县| 津市市| 宽城| 台东县| 木兰县| 响水县| 冕宁县| 江西省| 满城县| 福建省| 旬邑县| 闸北区|