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

溫馨提示×

溫馨提示×

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

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

Vue中nextTick有什么作用

發布時間:2020-10-19 14:27:38 來源:億速云 閱讀:205 作者:小新 欄目:web開發

這篇文章給大家分享的是有關Vue中nextTick有什么作用的內容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。

nextTick是Vue的一個核心功能,在Vue內部實現中也經常用到nextTick。但是,很多新手不理解nextTick的原理,甚至不清楚nextTick的作用。

那么,我們就先來看看nextTick是什么。

nextTick功能

看看官方文檔的描述:

在下次 DOM 更新循環結束之后執行延遲回調。在修改數據之后立即使用這個方法,獲取更新后的 DOM。

再看看官方示例:

// 修改數據
vm.msg = 'Hello'
// DOM 還沒有更新
Vue.nextTick(function () {
  // DOM 更新了
})

// 作為一個 Promise 使用 (2.1.0 起新增,詳見接下來的提示)
Vue.nextTick()
  .then(function () {
    // DOM 更新了
  })

2.1.0 起新增:如果沒有提供回調且在支持 Promise 的環境中,則返回一個 Promise。請注意 Vue 不自帶 Promise 的 polyfill,所以如果你的目標瀏覽器不原生支持 Promise (IE:你們都看我干嘛),你得自己提供 polyfill。

可以看到,nextTick主要功能就是改變數據后讓回調函數作用于dom更新后。很多人一看到這里就懵逼了,為什么需要在dom更新后再執行回調函數,我修改了數據后,不是dom自動就更新了嗎?

這個和JS中的Event Loop有關,網上教程不計其數,在此就不再贅述了。建議明白Event Loop后再繼續向下閱讀本文。

舉個實際的例子:

我們有個帶有分頁器的表格,每次翻頁需要選中第一項。正常情況下,我們想的是點擊翻頁器,向后臺獲取數據,更新表格數據,操縱表格API選中第一項。

但是,你會發現,表格數據是更新了,但是并沒有選中第一項。因為,你選中第一項時,雖然數據更新了,但是DOM并沒有更新。此時,你可以使用nextTick,在DOM更新后再操縱表格第一項的選中。

那么,nextTick到底做了什么了才能實現在DOM更新后執行回調函數?

源碼分析

nextTick的源碼位于src/core/util/next-tick.js,總計118行,十分的短小精悍,十分適合初次閱讀源碼的同學。

nextTick源碼主要分為兩塊:

1.能力檢測

2.根據能力檢測以不同方式執行回調隊列

能力檢測

這一塊其實很簡單,眾所周知,Event Loop分為宏任務(macro task)以及微任務( micro task),不管執行宏任務還是微任務,完成后都會進入下一個tick,并在兩個tick之間執行UI渲染。

但是,宏任務耗費的時間是大于微任務的,所以在瀏覽器支持的情況下,優先使用微任務。如果瀏覽器不支持微任務,使用宏任務;但是,各種宏任務之間也有效率的不同,需要根據瀏覽器的支持情況,使用不同的宏任務。

nextTick在能力檢測這一塊,就是遵循的這種思想。

// Determine (macro) task defer implementation.
// Technically setImmediate should be the ideal choice, but it's only available
// in IE. The only polyfill that consistently queues the callback after all DOM
// events triggered in the same loop is by using MessageChannel.
/* istanbul ignore if */
// 如果瀏覽器不支持Promise,使用宏任務來執行nextTick回調函數隊列
// 能力檢測,測試瀏覽器是否支持原生的setImmediate(setImmediate只在IE中有效)
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // 如果支持,宏任務( macro task)使用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 {
  /* istanbul ignore next */
  // 都不支持的情況下,使用setTimeout
  macroTimerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

首先,檢測瀏覽器是否支持setImmediate,不支持就使用MessageChannel,再不支持只能使用效率最差但是兼容性最好的setTimeout了。

之后,檢測瀏覽器是否支持Promise,如果支持,則使用Promise來執行回調函數隊列,畢竟微任務速度大于宏任務。如果不支持的話,就只能使用宏任務來執行回調函數隊列。

執行回調函數隊列

執行回調函數隊列的代碼剛好在一頭一尾

// 回調函數隊列
const callbacks = []
// 異步鎖
let pending = false

// 執行回調函數
function flushCallbacks () {
  // 重置異步鎖
  pending = false
  // 防止出現nextTick中包含nextTick時出現問題,在執行回調函數隊列前,提前復制備份,清空回調函數隊列
  const copies = callbacks.slice(0)
  callbacks.length = 0
  // 執行回調函數隊列
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

...

// 我們調用的nextTick函數
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()
    }
  }
  // $flow-disable-line
  // 2.1.0新增,如果沒有提供回調,并且支持Promise,返回一個Promise
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

總體流程就是,接收回調函數,將回調函數推入回調函數隊列中。

同時,在接收第一個回調函數時,執行能力檢測中對應的異步方法(異步方法中調用了回調函數隊列)。

如何保證只在接收第一個回調函數時執行異步方法?

nextTick源碼中使用了一個異步鎖的概念,即接收第一個回調函數時,先關上鎖,執行異步方法。此時,瀏覽器處于等待執行完同步代碼就執行異步代碼的情況。

打個比喻:相當于一群旅客準備上車,當第一個旅客上車的時候,車開始發動,準備出發,等到所有旅客都上車后,就可以正式開車了。

當然執行flushCallbacks函數時有個難以理解的點,即:為什么需要備份回調函數隊列?執行的也是備份的回調函數隊列?

因為,會出現這么一種情況:nextTick套用nextTick。如果flushCallbacks不做特殊處理,直接循環執行回調函數,會導致里面nextTick中的回調函數會進入回調隊列。這就相當于,下一個班車的旅客上了上一個班車。

實現一個簡易的nextTick

說了這么多,我們來實現一個簡單的nextTick:

let callbacks = []
let pending = false

function nextTick (cb) {
    callbacks.push(cb)

    if (!pending) {
        pending = true
        setTimeout(flushCallback, 0)
    }
}

function flushCallback () {
    pending = false
    let copies = callbacks.slice()
    callbacks.length = 0
    copies.forEach(copy => {
        copy()
    })
}

可以看到,在簡易版的nextTick中,通過nextTick接收回調函數,通過setTimeout來異步執行回調函數。通過這種方式,可以實現在下一個tick中執行回調函數,即在UI重新渲染后執行回調函數。

感謝各位的閱讀!關于Vue中nextTick有什么作用就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

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

AI

包头市| 乃东县| 阳东县| 赣州市| 尚志市| 三原县| 禹州市| 台中县| 上饶县| 西畴县| 融水| 郯城县| 叶城县| 虎林市| 文登市| 永兴县| 习水县| 拉萨市| 云林县| 澎湖县| 桐乡市| 永新县| 祁门县| 平阳县| 鄂尔多斯市| 行唐县| 玛多县| 陕西省| 横山县| 布拖县| 蕉岭县| 利辛县| 彩票| 铜梁县| 盐城市| 新干县| 铁力市| 浦东新区| 汪清县| 渭源县| 嵊泗县|