<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
用過Vue的朋友多多少少都知道$nextTick
~ 在正式講解nextTick之前,我想你應該清楚知道 Vue 在更新 DOM 時是非同步
執行的,因為接下來講解過程會結合元件更新一起講~ 事不宜遲,我們直進主題吧(本文以v2.6.14版本的Vue原始碼進行講解)
你真的瞭解nextTick嗎?來,直接上題~
<template> <div id="app"> <p ref="name">{{ name }}</p> <button @click="handleClick">修改name</button> </div> </template> <script> export default { name: 'App', data () { return { name: '井柏然' } }, mounted() { console.log('mounted', this.$refs.name.innerText) }, methods: { handleClick () { this.$nextTick(() => console.log('nextTick1', this.$refs.name.innerText)) this.name = 'jngboran' console.log('sync log', this.$refs.name.innerText) this.$nextTick(() => console.log('nextTick2', this.$refs.name.innerText)) } } } </script>
請問上述程式碼中,當點選按鈕“修改name”時,'nextTick1'
,'sync log'
,'nextTick2'
對應的this.$refs.name.innerText
分別會輸出什麼?注意,這裡列印的是DOM的innerText~(文章結尾處會貼出答案)
如果此時的你有非常堅定的答案,那你可以不用繼續往下看了~但如果你對自己的答案有所顧慮,那不如跟著我,接著往下看。相信你看完,不需要看到答案都能有個肯定的答案了~!
原始碼位於core/util/next-tick中。可以將其分為4個部分來看,直接上程式碼
callbacks
佇列、pending
狀態
const callbacks = [] // 存放cb的佇列 let pending = false // 是否馬上遍歷佇列,執行cb的標誌
flushCallbacks
遍歷callbacks執行每個cb
function flushCallbacks () { pending = false // 注意這裡,一旦執行,pending馬上被重置為false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() // 執行每個cb } }
nextTick
的非同步實現根據執行環境的支援程度採用不同的非同步實現策略
let timerFunc // nextTick非同步實現fn if (typeof Promise !== 'undefined' && isNative(Promise)) { // Promise方案 const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) // 將flushCallbacks包裝進Promise.then中 } isUsingMicroTask = true } else if (!isIE && typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || MutationObserver.toString() === '[object MutationObserverConstructor]' )) { // MutationObserver方案 let counter = 1 const observer = new MutationObserver(flushCallbacks) // 將flushCallbacks作為觀測變化的cb const textNode = document.createTextNode(String(counter)) // 建立文位元組點 // 觀測文位元組點變化 observer.observe(textNode, { characterData: true }) // timerFunc改變文位元組點的data,以觸發觀測的回撥flushCallbacks timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { // setImmediate方案 timerFunc = () => { setImmediate(flushCallbacks) } } else { // 最終降級方案setTimeout timerFunc = () => { setTimeout(flushCallbacks, 0) } }
這裡用個真實案例加深對MutationObserver
的理解。畢竟比起其他三種非同步方案,這個應該是大家最陌生的
const observer = new MutationObserver(() => console.log('觀測到文位元組點變化')) const textNode = document.createTextNode(String(1)) observer.observe(textNode, { characterData: true }) console.log('script start') setTimeout(() => console.log('timeout1')) textNode.data = String(2) // 這裡對文位元組點進行值的修改 console.log('script end')
知道對應的輸出會是怎麼樣的嗎?
script start
、script end
會在第一輪宏任務中執行,這點沒問題
setTimeout
會被放入下一輪宏任務執行
MutationObserver
是微任務,所以會在本輪宏任務後執行,所以先於setTimeout
結果如下圖:
cb
、Promise
方式
export function nextTick (cb?: Function, ctx?: Object) { let _resolve // 往全域性的callbacks佇列中新增cb callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { // 這裡是支援Promise的寫法 _resolve(ctx) } }) if (!pending) { pending = true // 執行timerFunc,在下一個Tick中執行callbacks中的所有cb timerFunc() } // 對Promise的實現,這也是我們使用時可以寫成nextTick.then的原因 if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }
pending
有什麼用,如何運作?$nextTick
,timerFunc
只會被執行一次this.$nextTick(() => console.log('nextTick1')) this.$nextTick(() => console.log('nextTick2'))
這裡如果有對Vue元件化、派發更新不是十分了解的朋友,可以先戳這裡,看圖解Vue響應式原理瞭解下Vue元件化和派發更新的相關內容再回來看噢~
Vue的非同步更新DOM其實也是使用nextTick
來實現的,跟我們平時使用的$nextTick其實是同一個~
這裡我們回顧一下,當我們改變一個屬性值的時候會發生什麼?
根據上圖派發更新過程,我們從watcher.update開時講起,以渲染Watcher為例,進入到queueWatcher
裡
// 用來存放Wathcer的佇列。注意,不要跟nextTick的callbacks搞混了,都是佇列,但用處不同~ const queue: Array<Watcher> = [] function queueWatcher (watcher: Watcher) { const id = watcher.id // 拿到Wathcer的id,這個id每個watcher都有且全域性唯一 if (has[id] == null) { // 避免新增重複wathcer,這也是非同步渲染的優化做法 has[id] = true if (!flushing) { queue.push(watcher) } if (!waiting) { waiting = true // 這裡把flushSchedulerQueue推進nextTick的callbacks佇列中 nextTick(flushSchedulerQueue) } } }
function flushSchedulerQueue () { currentFlushTimestamp = getNow() flushing = true let watcher, id // 排序保證先父後子執行更新,保證userWatcher在渲染Watcher前 queue.sort((a, b) => a.id - b.id) // 遍歷所有的需要派發更新的Watcher執行更新 for (index = 0; index < queue.length; index++) { watcher = queue[index] id = watcher.id has[id] = null // 真正執行派發更新,render -> update -> patch watcher.run() } }
相信經過上文對nextTick原始碼的剖析,我們已經揭開它神祕的面紗了。這時的你一定可以堅定地把答案說出來了~話不多說,我們一起核實下,看看是不是如你所想!
如圖所示,mounted
時候的innerText是“井柏然”的中文
接下來是點選按鈕後,列印結果如圖所示
沒錯,輸出結果如下(意不意外?驚不驚喜?)
sync log 井柏然
nextTick1 井柏然
nextTick2 jngboran
下面簡單分析一下每個輸出:
this.$nextTick(() => console.log('nextTick1', this.$refs.name.innerText)) this.name = 'jngboran' console.log('sync log', this.$refs.name.innerText) this.$nextTick(() => console.log('nextTick2', this.$refs.name.innerText))
sync log
:這個同步列印沒什麼好說了,相信大部分童鞋的疑問點都不在這裡。如果不清楚的童鞋可以先回顧一下EventLoop,這裡不多贅述了~
nextTick1
:注意其雖然是放在$nextTick
的回撥中,在下一個tick執行,但是他的位置是在this.name = 'jngboran'
的前。也就是說,他的cb會比App元件的派發更新(flushSchedulerQueue
)更先進入佇列,當nextTick1
列印時,App元件還未派發更新,所以拿到的還是舊的DOM值。
nextTick2
就不展開了,大家可以自行分析一下。相信大家對它應該是最肯定的,我們平時不就是這樣拿到更新後的DOM嗎?
最後來一張圖加深理解
寫在最後,nextTick其實在Vue中也算是比較核心的一個東西了。因為貫穿整個Vue應用的元件化、響應式的派發更新與其息息相關~深入理解nextTick的背後實現原理,不僅能讓你在面試的時候一展風采,更能讓你在日常開發工作中,少走彎路少踩坑!
以上就是Vue.nextTick純乾貨使用方法詳解的詳細內容,更多關於Vue.nextTick使用方法的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45