nextTick执行顺序小探

由 漆黑菌 于 2021年01月20日 发布

缘起

群友在群里问了下nextTick和updated的执行时间。我一开始觉得肯定是先updated再nextTick。然而实验结果稍微有点意外,要看注册nextTick函数的时间。在数据变动之前就比updated钩子先,之后则后。

原理

vue为了减少数据多次变动导致的watcher多次被过量更新,实际上处理数据变动产生的副作用是放在异步的timerFunc函数里操作的,劫持timerFunc为timeout并手动设置时间就会发现dom更新的时间被对应的推迟了。关于timerFunc函数的机制可以去翻源码,也很有趣。而vue在内部实际上依然是通过nextTick来触发timerFunc的,在源码中搜索timerFunc()发现只在nextTick中出现了一次。通过检索源代码可以发现,vue执行updated钩子的方法是flushSchedulerQueue。

  function queueWatcher (watcher) {
    var id = watcher.id;
    if (has[id] == null) {
      has[id] = true;
      if (!flushing) {
        queue.push(watcher);
      } else {
        // if already flushing, splice the watcher based on its id
        // if already past its id, it will be run next immediately.
        var i = queue.length - 1;
        while (i > index && queue[i].id > watcher.id) {
          i--;
        }
        queue.splice(i + 1, 0, watcher);
      }
      // queue the flush
      if (!waiting) {
        waiting = true;

        if (!config.async) {
          flushSchedulerQueue();
          return
        }
        nextTick(flushSchedulerQueue);
      }
    }
  }

调用flushSchedulerQueue有两种方式,但是根据config.async的注释,同步模式只给测试工具用。

    /**
     * Perform updates asynchronously. Intended to be used by Vue Test Utils
     * This will significantly reduce performance if set to false.
     */

正好就对上了,updated钩子也是通过注册在nextTick相关的callback函数们里执行的。所以nextTick注册的函数跟updated钩子的执行顺序和注册在数据变动之前还是之后有关。

总结

  1. vue处理数据变动产生的副作用是放在异步的timerFunc函数里操作的
  2. nextTick注册的函数和updated钩子的执行顺序和注册顺序有关
  3. 想要通过nextTick拿到数据变更后的dom建议在数据变更后再注册。