Vue原始碼學習(十三):nextTick()方法

2023-10-23 06:00:24

好傢伙,nextTick,

(...這玩意,不太常用)

 

1.什麼是nextTick

在Vue中,nextTick是一個用於非同步執行回撥函數的方法。

它在Vue更新DOM後被呼叫,以確保在下一次DOM更新渲染完成後執行回撥函數。

而事實上,我們把佇列處理的操作封裝到了nexrTick方法中.

 

實際上,Vue在更新DOM時是非同步執行的。

當你修改Vue範例的資料時,Vue會對依賴這些資料的虛擬DOM進行重新渲染,然後更新到真實的DOM上。

但是,DOM更新是在下一個事件迴圈中執行的,而不是立即執行。

所以,如果你想在DOM更新後執行一些操作,你就可以使用nextTick方法。

這個方法會將回撥函數推入到微任務佇列中,並在DOM更新後執行

這樣可以確保你在操作更新後的DOM,比如獲取元素的寬高等,而不是得到修改前的值。

 

舉一個非常簡單的例子

Vue.Mixin({ //全域性
            created: function b() {
                // console.log('b----2')
            }
        })
        let vm = new Vue({
            el: '#app', //編譯模板
            // data: {
            // },
            data() {
                // console.log(this)
                return {
                    msg:'牛肉',
                    arr: [1, 2, 3],
                }
            },
            created() {
                // console.log(555)
            }
        })
        console.log(vm.msg,"||直接列印msg的值")
        setTimeout(() => {
            //注意資料更新多次,vm._updata(vm._render()) 只需要執行一次
            vm.arr.push({b:5})
            vm.arr.push({b:6})
            console.log(vm.msg,"||計時器列印msg的值")
            vm.msg = '張三'
            vm.$nextTick(()=>{
                console.log(vm.msg,"||nextTick()方法列印msg的值")
            })
        }, 1000)

 

這裡

可以看出來

nextTick()方法中的console確實拿到了最新的值

 

 

2.程式碼實現

給vm原型新增$nextTick方法

2.1.initState.js

export function stateMixin(vm) {
    //列隊批次處理
    //1.處理vue自己的nextTick
    //2.使用者自己的
    vm.prototype.$nextTick = function (cb) {
        // console.log(cb)
        nextTick(cb)
    }
}

 

2.2.watcher.js

(此處為部分程式碼)

let queue = [] // 將需要批次更新的watcher 存放到一個列隊中
let has = {}
let pending = false
//陣列重置
function flushWatcher() {
    queue.forEach(item => {
        item.run()})
    queue = []
    has = {}
    pending = false
}
function queueWatcher(watcher) {
    let id = watcher.id // 每個元件都是同一個 watcher
    //    console.log(id) //去重
    if (has[id] == null) {//去重
        //列隊處理
        queue.push(watcher)//將wacher 新增到列隊中
        has[id] = true
        //防抖 :使用者觸發多次,只觸發一個 非同步,同步
        if (!pending) {
            //非同步:等待同步程式碼執行完畢之後,再執行
            // setTimeout(()=>{
            //   queue.forEach(item=>item.run())
            //   queue = []
            //   has = {}
            //   pending = false
            // },0)
            nextTick(flushWatcher) //  nextTick相當於定時器
        }
        pending = true
    }
}

 

2.3.nextTicks.js

let callback = []
let pending = false
function flush(){
   callback.forEach(cb =>cb())
   pending =false
}
let timerFunc
//處理相容問題

//判斷全域性物件中是否存在Promise
//看瀏覽器是否支援promise
if(Promise){
   timerFunc = ()=>{
       Promise.resolve().then(flush) //非同步處理
   }
}else if(MutationObserver){ //h5 非同步方法 他可以監聽 DOM 變化 ,監控完畢之後在來非同步更新
  let observe = new MutationObserver(flush)
  let textNode = document.createTextNode(1) //建立文字
  observe.observe(textNode,{characterData:true}) //觀測文字的內容
  timerFunc = ()=>{
   textNode.textContent = 2
  }
}else if(setImmediate){ //ie
   timerFunc = ()=>{
       setImmediate(flush) 
   }
}
export function nextTick(cb){
    // 1vue 2
   //  console.log(cb)
    //列隊 [cb1,cb2] 
    //此處,注意,我們要處理使用者的nextTick()也要處理vue自己的nextTick
    callback.push(cb)
    //Promise.then()  vue3
    
    if(!pending){
        timerFunc()   //這個方法就是非同步方法 但是 處理相容問題
        pending = true
    }
}