nextTick
是 Vue 的一个核心实现,在介绍 Vue 的 nextTick 之前,为了方便大家理解,先简单介绍一下 JS 的运行机制。
JS 运行机制
JS 执行是单线程的,它是基于事件循环的。事件循环大致分为以下几个步骤:
所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
主线程不断重复上面的第三步。
主线程的执行过程就是一个 tick,而所有的异步结果都是通过 “任务队列” 来调度。消息队列中存放的是一个个的任务(task)。规范中规定 task 分为两大类,分别是 macro task 和 micro task,并且每个 macro task 结束后,都要清空所有的 micro task。
简单通过一段代码演示他们的执行顺序:
在浏览器环境中:
常见的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate;
常见的 micro task 有 MutationObsever 和 Promise.then。
Vue 的实现
在 Vue 源码 2.5+ 后, 的实现单独有一个 JS 文件来维护它,它的源码并不多,总共也就 100 多行。接下来我们来看一下它的实现,在 中:
申明了 和 2 个变量,它们分别对应的是 micro task 的函数和 macro task 的函数。对于 macro task 的实现,优先检测是否支持原生 ,这是一个高版本 IE 和 Edge 才支持的特性,不支持的话再去检测是否支持原生的 ,如果也不支持的话就会降级为 ;而对于 micro task 的实现,则检测浏览器是否原生支持 Promise,不支持的话直接指向 macro task 的实现。
对外暴露了 2 个函数,先来看 。它的逻辑也很简单,把传入的回调函数 压入 数组,最后一次性地根据 条件执行 或者是 ,而它们都会在下一个 tick 执行 , 的逻辑非常简单,对 遍历,然后执行相应的回调函数。
这里使用 而不是直接在 中执行回调函数的原因是保证在同一个 tick 内多次执行 ,不会开启多个异步任务,而把这些异步任务都压成一个同步任务,在下一个 tick 执行完毕。
函数最后还有一段逻辑:
这是当 不传 参数的时候,提供一个 Promise 化的调用,比如:
当 函数执行,就会跳到 的逻辑中。
还对外暴露了 函数,它是对函数做一层包装,确保函数执行过程中对数据任意的修改,触发变化执行 的时候强制走 。比如对于一些 DOM 交互事件,如 绑定的事件回调函数的处理,会强制走 macro task。
总结
nextTick 接收一个回调函数作为参数,并将这个回调函数延迟到DOM更新后才执行;
使用场景:想要操作基于最新数据生成的DOM时,就将这个操作放在 nextTick 的回调中。
例如:从服务端接口去获取数据时,数据改变了,我们的某些方法依赖了数据变化后的 DOM 变化,就必须在 后执行。比如下面例子:
Vue.js 提供了 2 种调用 的方式,一种是全局 API ,一种是实例上的方法 ,无论我们使用哪一种,最后都是调用 中实现的 方法。
领取专属 10元无门槛券
私享最新 技术干货