Vue源码学习(十三):nextTick()方法

好家伙, 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
    }
}