vue异步更新过程

vue异步更新实现过程

  dep.notify()=>update()=> queueWatcher()=>nextTick()=>timerFunc(fn)=>flushCallbacks()=>flushSchedulerQueue()=>watcher.run()=>cb|render()
  1.数据发生变化时,触发收集器调用watcher的notify()方法。notify()方法执行对于dep(单个属性)的update()方法进行更新
  2.每个watcher对应一个update方法,update方法中调用watcher队列方方法,将所有的watcher放在一个队列函数中queueWatcher
  3.queueWatcher进行判断 如果已经进入队列则不重复添加,  如果队列已经执行刷新则    倒叙  遍历 通过当前的watcher.id 找到它大于队列中的watcher.id 插入到队列中,将队列刷新的方法传到nextTick()
  4.nextTick()将刷新队列的方法以回调函数的方式添加到 刷新callbacks数组中 通过timeFunc()方法将flushCallbacks()放在浏览器的异步队列中
  5.flushCallbacks()遍历callbacks的数据执行每一个callbacks函数,在callbacks数组中调用传入的刷新队列方法,执行watcher.run()方法触发render()渲染或者执行用户传入的回掉函数
       vue\src\core\observer\index.js
       class Watcher{
      set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) return
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      //触发依赖收集中的watch进行页面的更新
      dep.notify()
    }
   }
2.vue\src\core\observer\dep.js
         notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    if (process.env.NODE_ENV !== 'production' && !config.async) {
      // subs aren't sorted in scheduler if not running async
      // we need to sort them now to make sure they fire in correct
      // order
      subs.sort((a, b) => a.id - b.id)
    }
    for (let i = 0, l = subs.length; i < l; i++) {
      //通知所有的dep进行数据的更新,调用每一个watcher中的update()
      subs[i].update()
     }
    }
   3.vue\src\core\observer\watcher.js
      update () {
   /* istanbul ignore else */
     if (this.lazy) {//computed
       this.dirty = true
     } else if (this.sync) {//组件内部watch
       this.run()
     } else {
         //将当前的watcher添加到watcher的队列中
        queueWatcher(this)
     }
 }
   3.vue\src\core\observer\scheduler.js
      export function queueWatcher (watcher: Watcher) {
 const id = watcher.id
 if (has[id] == null) {
   has[id] = true
   if (!flushing) {//异步队列中共没有执行刷新的时候 则添加到队列中
     queue.push(watcher)
   } else { //队列已经刷新后则查找当前的watcher.id值大于队列中的watcher.id 插入到对应的位置
     // if already flushing, splice the watcher based on its id
     // if already past its id, it will be run next immediately.
     let 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 (process.env.NODE_ENV !== 'production' && !config.async) {
       flushSchedulerQueue()
       return
     }
     nextTick(flushSchedulerQueue) //将刷新队列的函数传到nextTick中
   }
//异步队列的函数
    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()

  // call component updated and activated hooks
  callActivatedHooks(activatedQueue)
  callUpdatedHooks(updatedQueue)

  // devtool hook
  /* istanbul ignore if */
  if (devtools && config.devtools) {
    devtools.emit('flush')
  }
}

4 vue\src\core\util\next-tick.js

export function nextTick (cb?: Function, ctx?: Object) {
  let _resolve
  callbacks.push(() => { //将刷新队列的函数以回调函数的方式添加到回调数组中
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  
  if (!pending) {
      //执行timeFunc将flushCallbacks传入
    pending = true
    timerFunc()
  }
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

//重要:timerFunc()函数将watcher添加到watcher队列的微任务中,在下次队列执行的时候则刷新微任务

if (typeof Promise !== 'undefined' && isNative(Promise)) {
  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.
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  // PhantomJS and iOS 7.x
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
  // Use MutationObserver where native Promise is not available,
  // e.g. PhantomJS, iOS7, Android 4.4
  // (#6466 MutationObserver is unreliable in IE11)
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // Fallback to setImmediate.
  // Technically it leverages the (macro) task queue,
  // but it is still a better choice than setTimeout.
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  // Fallback to setTimeout.
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

//flushCallbacks函数
function flushCallbacks () {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]() ///调用callbacks中存储的刷新队列的函数
  }
}
//执行flushSchedulerQueue()函数调用watcher.run()

vue\src\core\observer\watcher.js
   run () {
    if (this.active) {
      const value = this.get()
      if (
        value !== this.value ||
        // Deep watchers and watchers on Object/Arrays should fire even
        // when the value is the same, because the value may
        // have mutated.
        isObject(value) ||
        this.deep
      ) {
        // set new value
        const oldValue = this.value
        this.value = value
        if (this.user) {//执行用户回调
          const info = `callback for watcher "${this.expression}"`
          invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info)
        } else {//执行render函数
          this.cb.call(this.vm, value, oldValue)
        }
      }
    }
  }   

直接赋值更新和$nextTick()更新的区别:

直接赋值更新:之前存在更新队列中共如果存在则不执行更新 更新仍调用的是nextTick()函数
nextTick()更新:直接将回调函数传入更新队列 进行执行 不做是否存在的判断

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值