Vue中nextTick方法的实现
先来看一道题:
1 | setTimeout(function() { |
大致原因是,浏览器环境中有一个事件循环,它又包含多个任务队列,任务队列又分为两种:
macro-task
像script(整体代码)、setTimeout、setInterval、I/O、UI rendering等。
micro-task
如Promises(说的是原生的,polyfill在浏览器端通常是使用setTimeout实现的)、MutationObserver等。
JavaScript引擎首先从macrotask queue中取出第一个任务(通常是当前代码段),执行完毕后,将microtask queue中的所有任务取出,按顺序全部执行;然后再从macrotask queue中取下一个,执行完毕后,再次将microtask queue中的全部取出;循环往复,直到两个queue中的任务都取完。
为什么要说这些呢? 因为Vue文档里这么说了:
正是因为Vue里做了这样的优化,当Model层数据发生改变时,dom不会立刻去响应变化。所以当需要正确地获取响应了数据变化之后的DOM时,就要在DOM更新之后去获取。
而如何在当前事件队列执行完毕后尽快响应回调,才是今天要说的内容。借助开头所说的两种任务队列,要实现这个功能不难。Vue中用到了Promise.then和MutationObserver,只有当执行环境不支持时,才降级使用setTimeout代替。
先来个简单的Promises版的实现:
1 | const nextTick = function (fn) { |
再看看MutationObserver,它需要监听DOM的变化,我们可以做点小动作。
1 | const nextTick = function (fn) { |
以上实现的nextTick方法已经基本可以满足需求了。只是每次执行的时候都要重新建立一个新的microtask queue。我们可以对其做一个优化,使得同一队列的几个nextTick的回调在一个microtask queue里执行。
1 | const nextTick = (function () { |
实现方式大概就是这样吧。Vue里还做了一些兼容处理,仅此而已。
- 本文链接:https://vhtml.github.io/2017/06/05/js-nextTick/
- 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!