在 vue 源码中学到的队列执行机制,咔咔牛

Vue中源码中,用户使用的api在底层都会被解释为一个一个的任务,例如nextTickwatchercomputed等等,我们传入的回调函数都会被放入到一个队列中,然后在合适的时机执行;

而怎么判断这个队列中是否有数据需要执行?队列执行完毕后怎么清空队列?新任务如何加入队列?

今天就来看一下Vue的任务执行机制,来实现一个简单的任务队列,简化我们日常开发中的一些操作;

nextTick 怎么保证最后执行?

在我之前写的Vue源码解析系列中,有一篇文章是讲VuenextTick的实现原理的,这里就不再赘述了,有兴趣的可以去看看:【源码&库】Vue3 中的 nextTick 魔法背后的原理

Vue中,例如nextTick我们使用都是如下:

nextTick(() => {
  console.log('nextTick')
})

nextTick的背后,我们的回调函数最后其实是类似下面这样执行的:

// 伪代码
let queue = Promise.resolve();

const nextTick = (cb) => {
    queue = queue.then(cb);
};

而我们如果操作响应式数据的任务在Vue的背后类似如下面这样执行的:

// 伪代码
const count = ref(0);

// 依赖收集
effect(() => {
  console.log(count.value);
});

const effect = (cb) => {
  // 依赖收集
  queue.then(cb);
};

这样就可以保证nextTick一定在effect执行完毕之后执行,感兴趣的可以拿上面的代码尝试一下;

任务队列怎么实现?

上面的代码其实并没有任何队列的影子,只有一个Promise,队列的实现全靠Promise的特性,then可以注册多个回调函数,并且会按照注册的顺序执行;

而上面的伪代码只是说明怎么保证nextTick在最后执行,在Vue中是有一个专门的队列来存放这些任务的,完整伪代码如下:

let p = Promise.resolve();
const queue = [];

// job 就理解为一个函数即可
const queueJob = (job) => {
  queue.push(job);
  queueFlush();
};

const queueFlush = () => {
    p = p.then(flushJobs)
};

const flushJobs = () => {
    try {
        for (const job of queue) {
            job();
        }
    } finally {
        queue.length = 0;
    }
}

const nextTick = (cb) => {
    p = p.then(cb);
};

const effect = (cb) => {
    // 依赖收集
    queueJob(cb);
};

上面就是一个最小的Vue的队列执行,我们可以看到,每次执行完队列中的任务后,都会清空队列,这样就保证了每次执行的任务都是最新的;

Vue中的任务队列是按JS的宏任务为批次来执行的,整体逻辑非常清晰:

  • 一个宏任务中执行的过程中,会出现很多Vue的任务,这些任务会被放入到一个队列中;
  • 当一个任务入队后,会立即执行队列执行的函数,然后执行队列刷新函数;
  • 队列刷新函数是一个异步执行的函数,会在当前宏任务执行完毕后执行;
  • 宏任务执行完毕,根据JS的事件循环机制,会执行微任务,这个时候会自动执行队列刷新函数;
  • 队列刷新函数会执行队列中的所有任务,然后清空队列;
  • 根据Promise的特性,then可以注册多个回调函数,这个时候会按照注册的顺序执行回调函数;
  • nextTick的回调函数就是注册在Promisethen中的,所以nextTick的回调函数会队列刷新函数执行完毕后执行;

到这里我直呼内行,这个队列执行机制真的是妙啊,而且代码量也不多,非常简洁;

总结

VuenextTick全靠Promise的特性来实现,而Vue的内部也是一个一个的队列任务批量执行,内部用到了不少的知识点:

  • Promise的特性,这一点是我最惊艳的点,还可以这样用;
  • JS的事件循环机制,这个是必须要掌握的;
  • 队列数据结构,非常实用;

虽然被我简化之后代码很少,但是保留的核心思想,这个队列执行机制还是非常值得学习的,可以用到很多地方;

当然Vue内部还是有很多的细节的,感兴趣的可以去看看源码,谢谢大家的阅读,欢迎评论交流;

原文链接:https://juejin.cn/post/7245211688118108220 作者:田八

(0)
上一篇 2023年6月17日 上午10:00
下一篇 2023年6月17日 上午10:11

相关推荐

发表回复

登录后才能评论