vue中nextTick的原理

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

什么是Vue.nextTick()

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

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

源碼淺析

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

1、flushCallbacks () // 該函數(shù)是對(duì)callbacks進(jìn)行遍歷振定,然后執(zhí)行相應(yīng)的回調(diào)函數(shù)
2必怜、timerFunc // 異步執(zhí)行函數(shù) 用于異步延遲調(diào)用 flushCallbacks 函數(shù)
延遲調(diào)用優(yōu)先級(jí)如下:
Promise > MutationObserver > setImmediate > setTimeout

// 使用 MicroTask 的標(biāo)識(shí)符,這里是因?yàn)榛鸷?lt;=53時(shí) 無法觸發(fā)微任務(wù)
export let isUsingMicroTask = false 

 // 用來存儲(chǔ)所有需要執(zhí)行的回調(diào)函數(shù)
const callbacks = []

// 用來標(biāo)志是否正在執(zhí)行回調(diào)函數(shù)
let pending = false 

// 對(duì)callbacks進(jìn)行遍歷后频,然后執(zhí)行相應(yīng)的回調(diào)函數(shù)
function flushCallbacks () {
    pending = false
    // 這里拷貝的原因是:
    // 有的cb 執(zhí)行過程中又會(huì)往callbacks中加入內(nèi)容
    // 比如 $nextTick的回調(diào)函數(shù)里還有$nextTick
    // 后者的應(yīng)該放到下一輪的nextTick 中執(zhí)行
    // 所以拷貝一份當(dāng)前的梳庆,遍歷執(zhí)行完當(dāng)前的即可,避免無休止的執(zhí)行下去
    const copies = callbcks.slice(0)
    callbacks.length = 0
    for(let i = 0; i < copies.length; i++) {
        copies[i]()
    }
}

let timerFunc // 異步執(zhí)行函數(shù) 用于異步延遲調(diào)用 flushCallbacks 函數(shù)

// 優(yōu)先使用 Promise
if(typeof Promise !== 'undefined' && isNative(Promise)) {
    const p = Promise.resolve()
    timerFunc = () => {
        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)聽到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)) {
    timerFunc = () => {
        setImmediate(flushCallbacks)
    }
} else {
    timerFunc = () => {
        setTimeout(flushCallbacks, 0)
    }
}

3胧后、nextTick(cb? Function, ctx: Object) {}
next-tick.js 對(duì)外暴露了nextTick這一個(gè)參數(shù),所以每次調(diào)用Vue.nextTick時(shí)會(huì)執(zhí)行:

  • 把傳入的回調(diào)函數(shù)cb壓入callbacks數(shù)組
  • 執(zhí)行timerFunc函數(shù)抱环,延遲調(diào)用 flushCallbacks 函數(shù)
  • 遍歷執(zhí)行 callbacks 數(shù)組中的所有函數(shù)壳快,這里的 callbacks 沒有直接在 nextTick 中執(zhí)行回調(diào)函數(shù)的原因是保證在同一個(gè) tick 內(nèi)多次執(zhí)行nextTick,不會(huì)開啟多個(gè)異步任務(wù)镇草,而是把這些異步任務(wù)都?jí)撼梢粋€(gè)同步任務(wù)眶痰,在下一個(gè) tick 執(zhí)行完畢。
export function nextTick(cb? Function, ctx: Object) {
    let _resolve
    // cb 回調(diào)函數(shù)會(huì)統(tǒng)一處理壓入callbacks數(shù)組
    callbacks.push(() => {
        if(cb) {
            try {
                cb.call(ctx)
            } catch(e) {
                handleError(e, ctx, 'nextTick')
            }
        } else if (_resolve) {
            _resolve(ctx)
        }
    })
    
    // pending 為false 說明本輪事件循環(huán)中沒有執(zhí)行過timerFunc()
    if(!pending) {
        pending = true
        timerFunc()
    }
    
    // 當(dāng)不傳入 cb 參數(shù)時(shí)梯啤,提供一個(gè)promise化的調(diào)用 
    // 如nextTick().then(() => {})
    // 當(dāng)_resolve執(zhí)行時(shí)竖伯,就會(huì)跳轉(zhuǎn)到then邏輯中
    if(!cb && typeof Promise !== 'undefined') {
        return new Promise(resolve => {
            _resolve = resolve
        })
    }
}

詳見參考以下鏈接:
https://blog.csdn.net/qq_38290251/article/details/107550899
https://zhuanlan.zhihu.com/p/174396758
https://blog.csdn.net/chenzeze0707/article/details/90083725

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市因宇,隨后出現(xiàn)的幾起案子七婴,更是在濱河造成了極大的恐慌,老刑警劉巖察滑,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件打厘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡贺辰,警方通過查閱死者的電腦和手機(jī)户盯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饲化,“玉大人莽鸭,你說我怎么就攤上這事〕钥浚” “怎么了蒋川?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)撩笆。 經(jīng)常有香客問我捺球,道長(zhǎng),這世上最難降的妖魔是什么夕冲? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任氮兵,我火速辦了婚禮,結(jié)果婚禮上歹鱼,老公的妹妹穿的比我還像新娘泣栈。我一直安慰自己,他們只是感情好弥姻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布南片。 她就那樣靜靜地躺著,像睡著了一般庭敦。 火紅的嫁衣襯著肌膚如雪疼进。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天秧廉,我揣著相機(jī)與錄音伞广,去河邊找鬼。 笑死疼电,一個(gè)胖子當(dāng)著我的面吹牛嚼锄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蔽豺,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼区丑,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了修陡?” 一聲冷哼從身側(cè)響起沧侥,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎濒析,沒想到半個(gè)月后正什,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡号杏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年舰讹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了萍虽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖讥邻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情这揣,我是刑警寧澤涝开,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站护赊,受9級(jí)特大地震影響惠遏,放射性物質(zhì)發(fā)生泄漏砾跃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一节吮、第九天 我趴在偏房一處隱蔽的房頂上張望抽高。 院中可真熱鬧,春花似錦透绩、人聲如沸翘骂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碳竟。三九已至,卻和暖如春狸臣,著一層夾襖步出監(jiān)牢的瞬間莹桅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工固棚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留统翩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓此洲,卻偏偏與公主長(zhǎng)得像厂汗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呜师,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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

  • Vue中會(huì)用到一個(gè)nextTick()的方法娶桦,在Vue實(shí)例中更改數(shù)據(jù)之后需要改動(dòng)DOM,如果需要操作更新后的DOM...
    Taec0123閱讀 1,585評(píng)論 0 0
  • 1汁汗、為什么用Vue.nextTick() 首先來了解一下JS的運(yùn)行機(jī)制衷畦。 JS運(yùn)行機(jī)制(Event Loop) J...
    安北分享閱讀 6,339評(píng)論 1 14
  • vue官網(wǎng):可能你還沒有注意到,Vue 在更新 DOM 時(shí)是異步執(zhí)行的知牌。只要偵聽到數(shù)據(jù)變化祈争,Vue 將開啟一個(gè)隊(duì)列...
    夢(mèng)行烏托邦閱讀 496評(píng)論 0 0
  • 都會(huì)用 nextTick,也都知道 nextTick 作用是在下次 DOM 更新循環(huán)結(jié)束之后角寸,執(zhí)行延遲回調(diào)菩混,就可以...
    IT沐華閱讀 1,794評(píng)論 0 7
  • Vue中的nextTick涉及到Vue中DOM的異步更新沮峡,感覺很有意思,特意了解了一下亿柑。其中關(guān)于nextTick的...
    world_7735閱讀 1,110評(píng)論 0 21