美文网首页
vue源码分析(三十)核心函数之nextTick(二)

vue源码分析(三十)核心函数之nextTick(二)

作者: vue爱好者 | 来源:发表于2020-04-21 20:53 被阅读0次

    数据的更新从一次数据的赋值开始,我们先看一下我们的代码:

    this.post.title = 'new title'
    

    我们vue源码分析(二十)核心函数之nextTick(一)的最后曾经说过。

    关于 nextTick 触发的流程,我们应该是从数据的修改开始,首先是被 defineReactive拦截,之后触发Dep的notify,然后的Watcher的update函数,接着是queueWatcher函数的列队观察,最后才到nextTick

    接下来我们就看看 nextTick 之后又发生了什么,大致流程是这样的。

    1、 timerFun // 异步处理
    2、flushCallbacks // 调用回调函数
    3、 flushSchedulerQueue // 执行列队
    4、 watcher.run() // 执行观察程序

    timerFun (路径src/core/util/next-tick.js)

    const p = Promise.resolve()
      timerFunc = () => {
        p.then(flushCallbacks)
        // In problematic UIWebViews, Promise.then doesn't completely break, but
        // it can get stuck in a weird state where callbacks are pushed into the
        // microtask queue but the queue isn't being flushed, until the browser
        // needs to do some other work, e.g. handle a timer. Therefore we can
        // "force" the microtask queue to be flushed by adding an empty timer.
        //在有问题的UIWebViews中,Promise.then不会完全中断,但是
        //它可能会陷入一种奇怪的状态,回调被推到
        //微任务队列,但队列不会被刷新,直到浏览器
        //需要做一些其他的工作,例如处理计时器。因此我们可以
        //“强制”通过添加空计时器来刷新微任务队列。
        if (isIOS) setTimeout(noop)
      }
    

    这个就不多说了,代码很简单,就是执行了 flushCallbacks 函数。

    flushCallbacks

    function flushCallbacks () {
      pending = false
      const copies = callbacks.slice(0)
      callbacks.length = 0
      for (let i = 0; i < copies.length; i++) {
        copies[i]()
      }
    }
    

    这里就是调用之前nextTick函数push进来的函数。

    flushSchedulerQueue

    /**
     * Flush both queues and run the watchers.
     */
    function flushSchedulerQueue () {
      currentFlushTimestamp = getNow()
      flushing = true
      let watcher, id
    
      // Sort queue before flush.
      // This ensures that:
      // 1. Components are updated from parent to child. (because parent is always
      //    created before the child)
      // 2. A component's user watchers are run before its render watcher (because
      //    user watchers are created before the render watcher)
      // 3. If a component is destroyed during a parent component's watcher run,
      //    its watchers can be skipped.
      queue.sort((a, b) => a.id - b.id)
    
      // do not cache length because more watchers might be pushed
      // as we run existing watchers
      for (index = 0; index < queue.length; index++) {
        watcher = queue[index]
        if (watcher.before) {
          watcher.before()
        }
        id = watcher.id
        has[id] = null
        watcher.run()
        // in dev build, check and stop circular updates.
        if (process.env.NODE_ENV !== 'production' && has[id] != null) {
          circular[id] = (circular[id] || 0) + 1
          if (circular[id] > MAX_UPDATE_COUNT) {
            warn(
              'You may have an infinite update loop ' + (
                watcher.user
                  ? `in watcher with expression "${watcher.expression}"`
                  : `in a component render function.`
              ),
              watcher.vm
            )
            break
          }
        }
      }
      // keep copies of post queues before resetting state
      const activatedQueue = activatedChildren.slice()
      const updatedQueue = queue.slice()
    
      resetSchedulerState()
    
      // 调用组件更新并激活挂钩
      callActivatedHooks(activatedQueue)
      // 触发update生命周期
      callUpdatedHooks(updatedQueue)
    
      // devtool hook
      if (devtools && config.devtools) {
        devtools.emit('flush')
      }
    }
    

    我们来看看整个函数做了哪些操作:

    queue.sort((a, b) => a.id - b.id)
    

    可以看到首先是对整个列队进行了一个排序 id小的是先创建的,需要先执行。

    watcher.run()
    

    执行观察者的run函数。(执行run函数之后就会更新DOM)再执行用户自己的this.$nextTick()函数。

    /**
     * Reset the scheduler's state.
     */
    function resetSchedulerState () {
      index = queue.length = activatedChildren.length = 0
      has = {}
      if (process.env.NODE_ENV !== 'production') {
        circular = {}
      }
      waiting = flushing = false
    }
    

    resetSchedulerState 重置状态

    最后就不多说啦,就是调用 ‘Activated钩子’ 和 ‘触发update生命周期’。

    相关文章

      网友评论

          本文标题:vue源码分析(三十)核心函数之nextTick(二)

          本文链接:https://www.haomeiwen.com/subject/wzmkuhtx.html