Vue中$nextTick實(shí)現(xiàn)源碼解析
正文
先看一個簡單的問題
<template> <div @click="handleClick" ref="div">{{ text }}</div </template> <script> export default { data() { return { text: 'old' } }, methods: { handleClick() { this.text = 'new' console.log(this.$refs.div.innerText) } } } </script>
此時打印的結(jié)果是什么呢?是 'old'
。如果想讓它打印 'new'
,使用 nextTick
稍加改造就可以
this.$nextTick(() => { console.log(this.$refs.div.innerText) })
內(nèi)部實(shí)現(xiàn)
但是你想過它內(nèi)部是怎么實(shí)現(xiàn)的么,和我們寫 setTimeout
有什么區(qū)別呢?
因?yàn)槠綍r工作使用的是Vue2,所以我就以Vue2的最新版本2.6.14為例進(jìn)行分析,Vue3的實(shí)現(xiàn)應(yīng)該也是大同小異。
為了方便閱讀我刪掉了注釋,只關(guān)注最重要的實(shí)現(xiàn)
if (typeof Promise !== 'undefined' && isNative(Promise)) { const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) if (isIOS) setTimeout(noop) } isUsingMicroTask = true } else if (!isIE && typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || MutationObserver.toString() === '[object MutationObserverConstructor]' )) { let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { timerFunc = () => { setImmediate(flushCallbacks) } } else { timerFunc = () => { setTimeout(flushCallbacks, 0) } }
先大概掃一遍可知 $nextTick
主要是通過微任務(wù)來實(shí)現(xiàn)的,其實(shí)在2.5版本中,是采用宏任務(wù)與微任務(wù)相結(jié)合的方式實(shí)現(xiàn)的,但因?yàn)樵阡秩竞褪录幚碇幸恍┍容^怪異的行為(感興趣的話可以看下issue),所以最終統(tǒng)一采用了微任務(wù)。
先看第一塊:
if (typeof Promise !== 'undefined' && isNative(Promise)) { const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) if (isIOS) setTimeout(noop) } isUsingMicroTask = true }
如果可以使用 Promise
,就采用 promise.then
的方式去執(zhí)行回調(diào),將任務(wù)在下一個tick執(zhí)行。但是其中 if (isIOS) setTimeout(noop)
這句話是在做什么呢?在iOS >= 9.3.3的UIWebView中,定義的回調(diào)函數(shù)通過 Promise
的方式推到微任務(wù)隊(duì)列后,隊(duì)列不刷新,需要靠 setTimeout
來強(qiáng)制更新一下,noop
就是一個空函數(shù)。
再看第二塊:
else if (!isIE && typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || MutationObserver.toString() === '[object MutationObserverConstructor]' )) { let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true }
如果不能用 Promise
就降級使用 MutationObserver
。創(chuàng)建了一個文本節(jié)點(diǎn),并通過 observer
去觀察文本節(jié)點(diǎn)的變化。 characterData: true
這個配置就是當(dāng)文字變化的時候就會執(zhí)行回調(diào)。(counter + 1) % 2
會使文本節(jié)點(diǎn)的文字在 0
、 1
、 0
、 1
之間不同變化,這樣就會被 observer
觀察到。MutationObserver
也是微任務(wù)。
然后是第三塊:
else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { timerFunc = () => { setImmediate(flushCallbacks) } }
當(dāng)微任務(wù)都不被支持時,就要使用宏任務(wù)了。其實(shí)大多數(shù)情況下都不會走到這里,因?yàn)?setImmediate
并沒有成為正式的標(biāo)準(zhǔn),并且兼容性很差。
最后是第四塊:
else { timerFunc = () => { setTimeout(flushCallbacks, 0) } }
最后在所有方案都行不通時,只能采用 setTimeout
的方式。之所以有第三塊是因?yàn)殡m然都是宏任務(wù),但是 setImmediate
會比 setTimeout
快,所以MDN上才會說 setTimeout(fn, 0)
不能成為 setImmediate
的polyfill。就像作者在注釋中寫的那樣:它仍然是比 setTimeout
更好的選擇。
一步一步分析了 $nextTick
源碼后,你是否對它的用法理解更加透徹了呢?
以上就是Vue中$nextTick實(shí)現(xiàn)源碼解析的詳細(xì)內(nèi)容,更多關(guān)于Vue $nextTick解析的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue3?+?elementPlus?reset重置表單問題
這篇文章主要介紹了vue3?+?elementPlus?reset重置表單問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05Vue前端利用slice()方法實(shí)現(xiàn)分頁器
分頁功能是常見的需求之一,本文主要介紹了Vue前端利用slice()方法實(shí)現(xiàn)分頁器,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07Vue實(shí)用功能之實(shí)現(xiàn)拖拽元素、列表拖拽排序
在日常開發(fā)中,特別是管理端,經(jīng)常會遇到要實(shí)現(xiàn)拖拽排序的效果,下面這篇文章主要給大家介紹了關(guān)于Vue實(shí)用功能之實(shí)現(xiàn)拖拽元素、列表拖拽排序的相關(guān)資料,需要的朋友可以參考下2022-10-10解決Vue2.0 watch對象屬性變化監(jiān)聽不到的問題
今天小編就為大家分享一篇解決Vue2.0 watch對象屬性變化監(jiān)聽不到的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09Vue組件Draggable實(shí)現(xiàn)拖拽功能
這篇文章主要為大家詳細(xì)介紹了Vue組件Draggable實(shí)現(xiàn)拖拽功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-12-12vue.js中引入vuex儲存接口數(shù)據(jù)及調(diào)用的詳細(xì)流程
這篇文章主要給大家介紹了關(guān)于在vue.js中引入vuex儲存接口數(shù)據(jù)及調(diào)用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12