vue-$nextTick淺析

官方介紹

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

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

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

原理

Vue(2.6+) 在內(nèi)部對異步隊列嘗試使用原生的 Promise.then梭域、MutationObserver 和 setImmediate斑举,如果執(zhí)行環(huán)境不支持,則會采用 setTimeout(fn, 0) 代替病涨;
前兩者微任務(wù)富玷,后兩者宏任務(wù);

流程演示

Vue中,this.a="22222",數(shù)據(jù)被修改赎懦,簡易版流程(微任務(wù)版):

  1. a被修改雀鹃;
  2. 觸發(fā)a的setter,通知訂閱了a屬性的所有Watcher铲敛;
  3. Watcher收到通知褐澎,把該Watcher放到更新數(shù)組中;(過濾掉已存在數(shù)組中的相同Watcher伐蒋,保證同一個的Watcher在同一個tick只被執(zhí)行一次)
  4. 執(zhí)行其他同步代碼工三;
  5. 遍歷第三步收集到的更新數(shù)組,執(zhí)行里面所有Watcher的更新Dom方法先鱼;
  6. 執(zhí)行nextTick產(chǎn)生的微任務(wù)俭正,此時就能獲取到更新后的Dom;
  7. 渲染Dom焙畔,進(jìn)入下一個任務(wù)隊列;
nextTick.gif

測試代碼

<i id="a">{{a}}</i>
data() {
    return {
      a: 11
    }
  },
mounted() {
    let time_start = Date.now()
    consoleData('初始狀態(tài)')
    // 兩秒后修改a的值
    setTimeout(() => {
      console.log('(2s后修改a的值)')
      this.a = 22222
      consoleData('修改后馬上獲取')
      syncAwait()
      this.$nextTick(function () {
        consoleData('nextTick里獲取')
        console.log('(此時更新了Dom宏多,但視圖未渲染)')
        syncAwait()
        consoleData('執(zhí)行完nextTick')
        console.log('(執(zhí)行完該任務(wù)隊列,渲染視圖)')
      })
    }, 2000)
    // 打印元素的內(nèi)容與寬度
    function consoleData(title) {
      console.log(`${Date.now() - time_start}ms,${title}:值為${$('#a').text()},寬度為${$('#a').width()}`);
    }
    // 模擬兩秒的同步代碼執(zhí)行
    function syncAwait(time = 2000) {
      console.log('執(zhí)行2s的同步代碼')
      const startTime = Date.now();
      while (true) {
        if (Date.now() - startTime > time) { break; }
      }
    }
},
  
 0ms,初始狀態(tài):值為11,寬度為16
(2s后修改a的值)
 2006ms,修改后馬上獲取:值為11,寬度為16
 執(zhí)行2s的同步代碼
 4008ms,nextTick里獲取:值為22222,寬度為42
(此時更新了Dom肾请,但視圖未渲染)
 執(zhí)行2s的同步代碼
 6010ms,執(zhí)行完nextTick:值為22222,寬度為42
 (執(zhí)行完該任務(wù)隊列,渲染視圖)

注意此時dom更新了但未渲染


dom更新了未渲染.JPG

其他

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

  • 常見的微任務(wù):Promise更胖、MutationObserver铛铁、Object.observe(廢棄),以及nodejs中的process.nextTick饵逐;
  • 常見的宏任務(wù):setTimeout、setInterval倍权、setImmediate、I/O捞烟;

setImmediate

  • 該方法可能不會被批準(zhǔn)成為標(biāo)準(zhǔn)账锹,目前只有最新版本的 Internet Explorer 和Node.js 0.10+實現(xiàn)了該方法。它遇到了 Gecko(Firefox) 和Webkit (Google/Apple) 的阻力坷襟;
  • node.js里面setTimeout(fn, 0)會被強制改為setTimeout(fn, 1)奸柬,HTML 5里面setTimeout最小的時間限制是4ms婴程;

MutationObserver

  • 會在指定的DOM發(fā)生變化時被調(diào)用;
  • vue用的不是MutationObserver的DOM監(jiān)聽功能,而是用其生成微任務(wù)蒸绩;

Vue nextTick 實現(xiàn)順序修改歷史

  • 第一版的 nextTick 實現(xiàn)順序為 MutationObserver, setImmediate,setTimeout
  • 第一次修改是將 MutationObserver 替換為 postMessage
  • 第二次修改順序變?yōu)?Promise, MutationObserver, setTimeout.
  • 2.5.2+ Promise,setImmediate, Messagechannel, setTimeout
  • 2.6+ Promise患亿、MutationObserver押逼、setImmediate步藕、setTimeout

參考

vue nextTick原理
Vue.nextTick挑格,了解一下?
Vue nextTick 變遷史
setTimeout和setImmediate到底誰先執(zhí)行漂彤,本文讓你徹底理解Event Loop
掘金小冊-剖析 Vue.js 內(nèi)部運行機制

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市立润,隨后出現(xiàn)的幾起案子媳板,更是在濱河造成了極大的恐慌桑腮,老刑警劉巖拷肌,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旨巷,死亡現(xiàn)場離奇詭異,居然都是意外死亡采呐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門又固,熙熙樓的掌柜王于貴愁眉苦臉地迎上來煤率,“玉大人仰冠,你說我怎么就攤上這事蝶糯。” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵肢扯,是天一觀的道長担锤。 經(jīng)常有香客問我蔚晨,道長,這世上最難降的妖魔是什么肛循? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任育拨,我火速辦了婚禮谨履,結(jié)果婚禮上熬丧,老公的妹妹穿的比我還像新娘。我一直安慰自己析蝴,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布尝盼。 她就那樣靜靜地躺著佑菩,像睡著了一般盾沫。 火紅的嫁衣襯著肌膚如雪殿漠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天蕾哟,我揣著相機與錄音,去河邊找鬼谭确。 笑死票渠,一個胖子當(dāng)著我的面吹牛逐哈,可吹牛的內(nèi)容都是我干的问顷。 我是一名探鬼主播薯鼠,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼械蹋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了哗戈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤纱注,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后狞贱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜀涨,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年氧枣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片便监。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡碳想,死狀恐怖烧董,靈堂內(nèi)的尸體忽然破棺而出胧奔,到底是詐尸還是另有隱情,我是刑警寧澤葡盗,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布啡浊,位于F島的核電站,受9級特大地震影響巷嚣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜廷粒,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一红且、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧暇番,春花似錦、人聲如沸壁酬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽剂公。三九已至,卻和暖如春纲辽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拖吼。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留因块,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓涡上,卻偏偏與公主長得像拒名,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子增显,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355