Vue.nextTick原理

看着书签越来越多,是时候该整理一下了。如果知道 JS 的事件循环机制,那么 Vue.nextTick 的原理也就清楚了。

JS 的事件循环机制

回头看了过去的博文,居然没有 JS 事件循环机制的一点点内容。这里简单描述一下:

  1. JS 是单线程(Node 支持多线程),也就是说同一时刻 JS 引擎只能做一件事。
  2. JS 中的任务区分为同步和异步,可以理解为同步任务就必须一次处理完成,那如果有两个或多个同步任务呢,大多数多线程语言直接新开一个线程来进行处理就可以了。但是单线程语言就不行了,所以引入了异步编程的概念,异步任务先注册,等到同步任务执行完成后再进行调用,就是我们常说的回调方法。
  3. 但是一个程序同一时刻会存在多个同步任务和多个异步任务,所以又引入了任务队列,然后 JS 引擎就按照任务队列一个一个执行。
  4. 任务队列里面的任务同时存在同步任务和异步任务,那哪个优先级更高呢,所有 js 的任务又分为宏任务和微任务。

综上,JS 的事件循环就是执行一个宏任务后立刻执行任务期间加入的所有微任务,然后再开始新的一轮循环。如图:

JS事件循环

setTimeout 的误区:setTimeout 的回调不一定在指定时间后能执行。而是在指定时间后,将回调函数放入事件循环的队列中。

如果时间到了,JS 引擎还在执行同步任务,这个回调函数需要等待;如果当前事件循环的队列里还有其他回调,需要等其他回调执行完。

另外,setTimeout 0ms 也不是立刻执行,它有一个默认最小时间,为 4ms。

关于 Node:

1
2
3
4
5
6
7
// node
setTimeout(() => {
console.log("setTimeout");
}, 0);
setImmediate(() => {
console.log("setImmediate");
});

第一个宏任务之前在执行全局 Script,如果这个时间大于 4ms,这时 setTimeout 的回调函数已经放入队列,就先执行 setTimeout;如果准备时间小于 4ms,就会先执行 setImmediate。

Vue.nextTick

了解了 JS 的事件循环机制,再回过头来看 Vue.nextTick。那么就可以理解为 Vue.nextTick 就是在当然微任务队列插入了一个新的微任务,即在当然宏任务执行完成之后执行。

所以大多数 nextTick 的使用场景都是获取组件实例、DOM 元素等。因为组件实例化、渲染 DOM 元素等操作都是异步任务,所以归根结底,Vue.nextTick 原理就是 JS 事件循环机制。

[越努力,越幸运!]