今天就跟大家聊聊有關(guān)nextTick怎么在vue中使用,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
成都創(chuàng)新互聯(lián)公司堅(jiān)信:善待客戶,將會(huì)成為終身客戶。我們能堅(jiān)持多年,是因?yàn)槲覀円恢笨芍档眯刨嚒N覀儚牟缓鲇瞥踉L客戶,我們用心做好本職工作,不忘初心,方得始終。十多年網(wǎng)站建設(shè)經(jīng)驗(yàn)成都創(chuàng)新互聯(lián)公司是成都老牌網(wǎng)站營(yíng)銷服務(wù)商,為您提供成都網(wǎng)站建設(shè)、成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)、html5、網(wǎng)站制作、成都品牌網(wǎng)站建設(shè)、小程序制作服務(wù),給眾多知名企業(yè)提供過(guò)好品質(zhì)的建站服務(wù)。
父組件:
.....
子組件
......
問(wèn)題分析:
原因vue官網(wǎng)上有解析( cn.vuejs.org/v2/guide/re… )
可能你還沒有注意到,Vue 異步執(zhí)行 DOM 更新。只要觀察到數(shù)據(jù)變化,Vue 將開啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)改變。如果同一個(gè) watcher 被多次觸發(fā),只會(huì)被推入到隊(duì)列中一次。這種在緩沖時(shí)去除重復(fù)數(shù)據(jù)對(duì)于避免不必要的計(jì)算和 DOM 操作上非常重要。然后,在下一個(gè)的事件循環(huán)“tick”中,Vue 刷新隊(duì)列并執(zhí)行實(shí)際 (已去重的) 工作。Vue 在內(nèi)部嘗試對(duì)異步隊(duì)列使用原生的 Promise.then 和 MessageChannel,如果執(zhí)行環(huán)境不支持,會(huì)采用 setTimeout(fn, 0) 代替。
這句話就是說(shuō),當(dāng)我們?cè)诟附M件設(shè)置this.name=name的時(shí)候,vue并不會(huì)直接更新到子組件中(dom的更新也一樣未立即執(zhí)行),而是把這些更新操作全部放入到一個(gè)隊(duì)列當(dāng)中,同個(gè)組件的所有這些賦值操作,都作為一個(gè)watcher的更新操作放入這個(gè)隊(duì)列當(dāng)中,然后等到事件循環(huán)結(jié)束的時(shí)候,一次性從這個(gè)隊(duì)列當(dāng)中獲取所有的wathcer執(zhí)行更新操作。在我們這個(gè)例子當(dāng)中,就是我們?cè)谡{(diào)用show的時(shí)候,實(shí)際上,我們的this.name=name并未真正執(zhí)行,而是被放入隊(duì)列中。vue的這種做法是基于優(yōu)化而做的,毋庸置疑,不然我們?nèi)绻衝多個(gè)賦值vue就執(zhí)行n多個(gè)dom更新,那效率將會(huì)非常的低效和不可取的。
下文中的更新操作指對(duì)data的值進(jìn)行更新的操作,在vue中,都會(huì)被放入隊(duì)列異步執(zhí)行。
解決方案:
1、 使用nextTick來(lái)延遲執(zhí)行show方法(籠統(tǒng)得說(shuō),執(zhí)行所有需要在數(shù)據(jù)真正更新后的操作
通過(guò)上面的分析我們知道,我們的所有的對(duì)vue實(shí)例的更新操作,都會(huì)先被放入一個(gè)隊(duì)列當(dāng)中,延遲異步執(zhí)行,這些異步操作,要么是microtask,要么是macrotask(是microtask還是macroktask取決于環(huán)境,nextTick的源碼中有所體現(xiàn)),根據(jù)事件循環(huán)機(jī)制,先入隊(duì)列的先執(zhí)行,所以如果我們?cè)趎extTick當(dāng)中執(zhí)行操作就會(huì)變成這樣。
2、 使用setTimeout來(lái)延遲執(zhí)行show方法,原理同上
所以我們的解決方法可以是:
this.name = data.name setTimeout(() => { this.$refs.pop.show() })
或者
this.name = data.name this.$nextTick(() => { this.$refs.pop.show() })
nextTick的實(shí)現(xiàn)原理
其實(shí)nextTick的實(shí)現(xiàn)原理是挺簡(jiǎn)單的,簡(jiǎn)單點(diǎn)說(shuō),就是實(shí)現(xiàn)異步,通過(guò)不同的執(zhí)行環(huán)境,用不同的方式來(lái)實(shí)現(xiàn),保證nextTick里面的回調(diào)函數(shù)能夠異步執(zhí)行。為什么要這么做呢?因?yàn)関ue對(duì)dom的更新也是異步的呀。
下面貼出源碼:
/** * 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 }) } } })()
首先我們看到這個(gè)是利用了閉包的特性,返回queueNextTick,所以我們實(shí)際調(diào)用的nextTick其實(shí)就是調(diào)用queueNextTick,一調(diào)用這個(gè)方法,就會(huì)把nextTick的回調(diào)放入隊(duì)列callbacks當(dāng)中,等到合適的時(shí)機(jī),會(huì)將callbacks中的所有回調(diào)取出來(lái)執(zhí)行,以達(dá)到延遲執(zhí)行的目的。為啥要用閉包呢,我覺得有兩個(gè)原因:
1、共享變量,比如callbacks、pending和timerFunc。
2、避免反復(fù)判斷,即是避免反復(fù)判斷timerFunc是利用Promise還是利用MutationObserver或是setTimeout來(lái)實(shí)現(xiàn)異步,這是函數(shù)柯里化的一種運(yùn)用。
這里有兩個(gè)最主要的方法需要解釋下:
1、 nextTickHandler
這個(gè)函數(shù),就是把隊(duì)列中的回調(diào),全部取出來(lái)執(zhí)行,類似于microtask的任務(wù)隊(duì)列。我們通過(guò)調(diào)用Vue.$nextTick就會(huì)把回調(diào)全部放入這個(gè)隊(duì)列當(dāng)中,等到要執(zhí)行的時(shí)候,調(diào)用nextTickHandler全部取出來(lái)執(zhí)行。
2、 timerFunc
這個(gè)變量,它的作用就是通過(guò)Promise/Mutationobserver/Settimeout把nextTickHandler放入到真正的任務(wù)隊(duì)列當(dāng)中,等到事件循環(huán)結(jié)束,就從任務(wù)隊(duì)列當(dāng)中取出nextTickHandler來(lái)執(zhí)行,nextTickHandler一執(zhí)行,callbacks里面的所有回調(diào)就會(huì)被取出來(lái)執(zhí)行來(lái),這樣就達(dá)到來(lái)延遲執(zhí)行nextTick傳的回調(diào)的效果。
通過(guò)這個(gè)簡(jiǎn)單的源碼分析,我們可以得出兩個(gè)結(jié)論
1、nextTick會(huì)根據(jù)不同的執(zhí)行環(huán)境,異步任務(wù)可能為microtask或者macrotask,而不是固定不變的。所以,如果你想讓nextTick里面的異步任務(wù)統(tǒng)統(tǒng)看成是microtask的話,你會(huì)遇到坑的。
2、nextTick的并不能保證一定能獲取得到更新后的dom,這取決于你是先進(jìn)行數(shù)據(jù)賦值還是先調(diào)用nextTick。比如:
new Vue({ el: '#app', data() { return { id: 2 } }, created() { }, mounted() { this.$nextTick(() => { console.log(document.getElementById('id').textContent) // 這里打印出來(lái)的是2,因?yàn)橄日{(diào)用了nextTick }) this.id = 3 } })
看完上述內(nèi)容,你們對(duì)nextTick怎么在vue中使用有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。