VUE的nextTick的使用及原理

nextTick

  • 下面了解下nextTick的主要應(yīng)用的場(chǎng)景及原因俺陋。

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

created()鉤子函數(shù)執(zhí)行的時(shí)候DOM 其實(shí)并未進(jìn)行任何渲染,而此時(shí)進(jìn)行DOM操作無(wú)異于徒勞,所以要將DOM操作的js代碼放進(jìn)Vue.nextTick()的回調(diào)函數(shù)中箩退。與之對(duì)應(yīng)的就是mounted()鉤子函數(shù)丑罪,因?yàn)樵撱^子函數(shù)執(zhí)行時(shí)所有的DOM掛載和渲染都已完成,此時(shí)在該鉤子函數(shù)中進(jìn)行任何DOM操作都不會(huì)有問(wèn)題 杰扫。

在數(shù)據(jù)變化后要執(zhí)行的某個(gè)操作队寇,而這個(gè)操作需要使用隨數(shù)據(jù)改變而改變的DOM結(jié)構(gòu)的時(shí)候,這個(gè)操作都應(yīng)該放進(jìn)Vue.nextTick()的回調(diào)函數(shù)中章姓。
https://zhuanlan.zhihu.com/p/69641232

  • nextTick 按我的理解佳遣,就是設(shè)置一個(gè)回調(diào)炭序,用于異步執(zhí)行

異步執(zhí)行,比如苍日,就是把你設(shè)置的回調(diào)放在 setTimeout 中執(zhí)行惭聂,這樣就算異步了,等待當(dāng)時(shí)同步代碼執(zhí)行完畢再執(zhí)行

但是相恃,每設(shè)置一個(gè) nextTick 就新建一個(gè) setTimeout 又不實(shí)際辜纲,

畢竟一個(gè) setTimeout 是異步,兩個(gè)setTimeout 也是異步拦耐,兩個(gè)都要等在 同步代碼執(zhí)行完畢之后才執(zhí)行

那我直接只設(shè)置一個(gè) setTimeout 不就好了

  • 那一個(gè) setTimeout 怎么執(zhí)行多個(gè)回調(diào)呢耕腾?
    1 存在 回調(diào)數(shù)組 里。每次調(diào)用 nextTick杀糯,便往數(shù)組里面 push 設(shè)置的回調(diào)

    2 只注冊(cè)一個(gè) setTimeout扫俺,時(shí)間為0,用于遍歷 回調(diào)數(shù)組固翰,然后逐個(gè)執(zhí)行子項(xiàng)

    3 同步代碼執(zhí)行完畢狼纬,setTimeout 自然會(huì)執(zhí)行
    Vue 不止使用 setTimeout
    Vue的 nextTick 也是只用setTimeout 嗎,不是的骂际,這里便會(huì)涉及到 javascript 的 宏微任務(wù)

關(guān)于宏微任務(wù)疗琉,簡(jiǎn)單說(shuō)一下

1 兩者區(qū)別在于執(zhí)行權(quán)重的問(wèn)題,微任務(wù)優(yōu)先級(jí)要比宏任務(wù)高

2 宏任務(wù) 和 微任務(wù) 合作完成一個(gè) Event Loop

3 執(zhí)行一個(gè) 宏任務(wù)歉铝,便會(huì)執(zhí)行一列微任務(wù)盈简。接著執(zhí)行另一個(gè)宏任務(wù)...(循環(huán)往復(fù),比如一個(gè)setTimeout 就是一個(gè)宏任務(wù))

Vue 2.4 以前太示,只使用 微任務(wù)柠贤,因?yàn)槲⑷蝿?wù)執(zhí)行優(yōu)先級(jí)高

Vue 2.5.3 之后,分成了 宏任務(wù) 和 微任務(wù)类缤,為了解決連續(xù)事件帶來(lái)的問(wèn)題臼勉,比如冒泡(至于為什么,會(huì)有一篇文章說(shuō)明)

Vue 2.6 呀非,又只使用微任務(wù)坚俗,因?yàn)橄氲搅似渌k法解決連續(xù)事件的問(wèn)題

Vue 的 宏微任務(wù) 并不算是嚴(yán)格意義上的宏微任務(wù),是種兼容的寫法岸裙。

  • Vue 使用了 nextTick 進(jìn)行統(tǒng)一更新
    你應(yīng)該知道猖败,即使在 Vue 中多么頻繁地修改數(shù)據(jù),最后 Vue 頁(yè)面只會(huì)更新一次

這是 Vue 和 nextTick 合作產(chǎn)生的結(jié)果降允,但又并不只是 nextTick 起作用

比如
數(shù)據(jù) name 被 頁(yè)面引用恩闻,name 會(huì)收集到 頁(yè)面的 watcher
name 被修改時(shí),會(huì)通知所有收集到的 watcher 進(jìn)行更新(watcher.update)
this.name = 2
this.name = 3
this.name = 4
name 一時(shí)間被修改三次時(shí)剧董,按道理應(yīng)該會(huì)通知三次 watcher 更新幢尚,那么頁(yè)面會(huì)更新三次
但是最后只會(huì)更新一次

就是因?yàn)樗麄兊?code>合作

設(shè)置 nextTick 回調(diào) + 過(guò)濾 watcher

當(dāng)數(shù)據(jù)變化后破停,把 watcher.update 函數(shù)存放進(jìn) nextTick 的 回調(diào)數(shù)組中,并且會(huì)做過(guò)濾尉剩。

通過(guò) watcher.id 來(lái)判斷 回調(diào)數(shù)組 中是否已經(jīng)存在這個(gè) watcher 的更新函數(shù)

不存在真慢,才 push

之后 nextTick 遍歷回調(diào)數(shù)組,便會(huì)執(zhí)行了更新

所以
當(dāng)三次修改數(shù)據(jù)的時(shí)候理茎,會(huì)準(zhǔn)備 push進(jìn) 回調(diào)數(shù)組 三個(gè) watcher.update黑界,但是只有第一次是 push 成功的,其他的會(huì)被過(guò)濾掉
所以皂林,不管你修改多少次數(shù)據(jù)朗鸠,nextTick 的回調(diào)數(shù)組中只存在唯一一個(gè) watcher.update,從而頁(yè)面只會(huì)更新一次
源碼

/**
 * Defer a task to execute it asynchronously.
 */
export const nextTick = (function () {
  const callbacks = []
  let pending = false
  let timerFunc

  function nextTickHandler () {
    pending = false
    const copies = callbacks.slice(0)
    callbacks.length = 0
    for (let i = 0; i < copies.length; i++) {
      copies[i]()
    }
  }

  // the nextTick behavior leverages the microtask queue, which can be accessed
  // via either native Promise.then or MutationObserver.
  // MutationObserver has wider support, however it is seriously bugged in
  // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
  // completely stops working after triggering a few times... so, if native
  // Promise is available, we will use it:
  /* istanbul ignore if */
  if (typeof Promise !== 'undefined' && isNative(Promise)) {
    var p = Promise.resolve()
    var logError = err => { console.error(err) }
    timerFunc = () => {
      p.then(nextTickHandler).catch(logError)
      // in problematic UIWebViews, Promise.then doesn't completely break, but
      // it can get stuck in a weird state where callbacks are pushed into the
      // microtask queue but the queue isn't being flushed, until the browser
      // needs to do some other work, e.g. handle a timer. Therefore we can
      // "force" the microtask queue to be flushed by adding an empty timer.
      if (isIOS) setTimeout(noop)
    }
  } else if (!isIE && typeof MutationObserver !== 'undefined' && (
    isNative(MutationObserver) ||
    // PhantomJS and iOS 7.x
    MutationObserver.toString() === '[object MutationObserverConstructor]'
  )) {
    // use MutationObserver where native Promise is not available,
    // e.g. PhantomJS, iOS7, Android 4.4
    var counter = 1
    var observer = new MutationObserver(nextTickHandler)
    var textNode = document.createTextNode(String(counter))
    observer.observe(textNode, {
      characterData: true
    })
    timerFunc = () => {
      counter = (counter + 1) % 2
      textNode.data = String(counter)
    }
  } else {
    // fallback to setTimeout
    /* istanbul ignore next */
    timerFunc = () => {
      setTimeout(nextTickHandler, 0)
    }
  }

  return function queueNextTick (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
      timerFunc()
    }
    if (!cb && typeof Promise !== 'undefined') {
      return new Promise((resolve, reject) => {
        _resolve = resolve
      })
    }
  }
})()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末础倍,一起剝皮案震驚了整個(gè)濱河市烛占,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沟启,老刑警劉巖忆家,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異美浦,居然都是意外死亡弦赖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門浦辨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人沼沈,你說(shuō)我怎么就攤上這事流酬。” “怎么了列另?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵芽腾,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我页衙,道長(zhǎng)摊滔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任店乐,我火速辦了婚禮艰躺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘眨八。我一直安慰自己腺兴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布廉侧。 她就那樣靜靜地躺著页响,像睡著了一般篓足。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上闰蚕,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天栈拖,我揣著相機(jī)與錄音,去河邊找鬼没陡。 笑死涩哟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诗鸭。 我是一名探鬼主播染簇,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼强岸!你這毒婦竟也來(lái)了锻弓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蝌箍,失蹤者是張志新(化名)和其女友劉穎青灼,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妓盲,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杂拨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悯衬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弹沽。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖筋粗,靈堂內(nèi)的尸體忽然破棺而出策橘,到底是詐尸還是另有隱情,我是刑警寧澤娜亿,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布丽已,位于F島的核電站,受9級(jí)特大地震影響买决,放射性物質(zhì)發(fā)生泄漏沛婴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一督赤、第九天 我趴在偏房一處隱蔽的房頂上張望嘁灯。 院中可真熱鬧,春花似錦够挂、人聲如沸旁仿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)枯冈。三九已至毅贮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尘奏,已是汗流浹背滩褥。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炫加,地道東北人瑰煎。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像俗孝,于是被迫代替她去往敵國(guó)和親酒甸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容