Vue NextTick介紹與使用原理
一、NextTick是什么
定義
在下次 DOM 更新循環(huán)結束之后執(zhí)行延遲回調。在修改數據之后立即使用這個方法,獲取更新后的 DOM。
理解
Vue在更新DOM時時異步執(zhí)行的。當數據發(fā)生變化時,Vue將開啟一個異步更新隊列,視圖需要等隊列中所有數據變化完成之后,再統(tǒng)一進行更新。
舉例:
<body> <div id="app"> <div> <button id="firstBtn" @click="testClick()" ref="aa">{{testMsg}}</button> </div> </div> </body> </html> <script> new Vue({ el:'#app', data () { return { testMsg:"原始值", } }, methods:{ testClick:function(){ this.testMsg="修改后的值"; console.log(this.$refs.aa.innerText); //that.$refs.aa獲取指定DOM,輸出:原始值 console.log(document.querySelector('#firstBtn').innerText) } } }) </script>
這時候想獲取頁面最新的DOM節(jié)點,卻發(fā)現獲取到的仍然是舊值。
這是因為message數據在發(fā)現變化的時候,vue并不會立刻去更新Dom,而是將修改數據的操作放在了一個異步操作隊列中,如果我們一直修改相同數據,異步操作隊列還會進行去重,等待同一事件循環(huán)中的所有數據變化完成之后,會將隊列中的事件拿來進行處理,進行DOM的更新。
為什么要有nexttick
{{num}} for(let i=0; i<100000; i++){ num = i }
如果沒有 nextTick 更新機制,那么 num 每次更新值都會觸發(fā)視圖更新(上面這段代碼也就是會更新10萬次視圖),有了nextTick機制,只需要更新一次,所以nextTick本質是一種優(yōu)化策略。
二、使用場景
this.testMsg="修改后的值"; console.log(this.$refs.aa.innerText); //that.$refs.aa獲取指定DOM,輸出:原始值 Vue.nextTick(function(){ console.log(document.querySelector('#firstBtn').innerText) })
組件內使用vm.$nextTick() 實例方法只需要通過this. $nextTick(),并且回調函數中的 this 將自動綁定到當前的 Vue 實例上
this.message = '修改后的值' console.log(this.$refs.aa.innerText) // => '原始的值' this.$nextTick(function () { console.log(this.$refs.aa.innerText) // => '修改后的值' })
$nextTick() 會返回一個 Promise 對象,可以是用async/await完成相同作用的事情
await Vue.nextTick() console.log(document.querySelector('#firstBtn').innerText)
三、實現原理
- callbacks也就是異步操作隊列
- callbacks新增回調函數后又執(zhí)行了timerFunc函數,pending是用來標識同一個時間只能執(zhí)行一次。
export function nextTick(cb?: Function, ctx?: Object) { let _resolve; // cb 回調函數會經統(tǒng)一處理壓入 callbacks 數組 callbacks.push(() => { if (cb) { // 給 cb 回調函數執(zhí)行加上了 try-catch 錯誤處理 try { cb.call(ctx); } catch (e) { handleError(e, ctx, 'nextTick'); } } else if (_resolve) { _resolve(ctx); } }); // 執(zhí)行異步延遲函數 timerFunc if (!pending) { pending = true; timerFunc(); } // 當 nextTick 沒有傳入函數參數的時候,返回一個 Promise 化的調用 if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve; }); } }
- timerFunc函數定義,這里是根據當前環(huán)境支持什么方法則確定調用哪個,分別有:Promise.then、MutationObserver、setImmediate、setTimeout;
- 通過上面任意一種方法,進行降級操作
export let isUsingMicroTask = false if (typeof Promise !== 'undefined' && isNative(Promise)) { //判斷1:是否原生支持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]' )) { //判斷2:是否原生支持MutationObserver 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)) { //判斷3:是否原生支持setImmediate timerFunc = () => { setImmediate(flushCallbacks) } } else { //判斷4:上面都不行,直接用setTimeout timerFunc = () => { setTimeout(flushCallbacks, 0) } }
- 無論是微任務還是宏任務,都會放到flushCallbacks使用
- 這里將callbacks里面的函數復制一份,同時callbacks置空
- 依次執(zhí)行callbacks里面的函數
function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } }
小結:
- 把回調函數放入callbacks等待執(zhí)行;
- 將執(zhí)行函數放到微任務或者宏任務中;
- 事件循環(huán)到了微任務或者宏任務,執(zhí)行函數依次執(zhí)行callbacks中的回調。
到此這篇關于Vue NextTick介紹與使用原理的文章就介紹到這了,更多相關Vue NextTick內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue在自定義組件上使用v-model和.sync的方法實例
自定義組件的v-model和.sync修飾符其實本質上都是vue的語法糖,用于實現父子組件的"數據"雙向綁定,下面這篇文章主要給大家介紹了關于vue在自定義組件上使用v-model和.sync的相關資料,需要的朋友可以參考下2022-07-07