Vue.js 源码分析-nextTick

373 阅读2分钟

「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战

nextTick

首先先让我们回顾一下:异步更新队列-nextTick()

  • Vue 更新 Dom 是异步执行的,批量的

    • 在下次 DOM 更新循环结束之后执行延迟回调。再修改数据之后立即使用这个方法,获取更新后的DOM。
  • vm.$nextTick(function() {/*操作DOM}*/ }) / Vue.nextTick(function(){})

  • 静态方法 nextTick

    • 路径: src\core\global-api\index.js
       // 静态方法 nextTick
        Vue.nextTick = nextTick
      
  • 实例方法 nextTick

    • 路径:src\core\instance\index.js
      // 混入 render
      // $nextTick/_render
      renderMixin(Vue)
      
    • renderMixin
      Vue.prototype.$nextTick = function (fn: Function) {
          return nextTick(fn, this)
      }
      

对比两个方法我们发现最终调用的都是nextTick这个方法,也就是说其实我们只需要研究nextTick就好了,e。。。。有点绕哈,其实就是src\core\util\next-tick.js下的nextTick函数就是了

nextTick

参数: - cb?: Function - 可选参数,回调函数 - ctx?: Object - 上下文,一般就是 vue 实例

  1. 把 cb 加上异常处理存入 callbacks 数组中,所有用户传入的函数,Vue认为都是危险的,都会给我们加上 try/catch(严谨啊!)
callbacks.push(() => {
    if (cb) {
      try {
        // 调用 cb()
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  1. 判断队列是否被处理,如果队列没有被处理,会设置 pendingtrue表示队列为正在处理中
if (!pending) {
    pending = true
    // 调用
    timerFunc()
  }
  • timerFunc:我们可以看见,这个函数中调用了 Promise,调用flushCallbacks函数
    const p = Promise.resolve()
     timerFunc = () => {
        p.then(flushCallbacks)
        if (isIOS) setTimeout(noop)
      }
    
    • flushCallbacks:设置函数已经执行结束,并且复制一次callbacks数组后清空,遍历回调函数并执行。
      function flushCallbacks () {
        pending = false
        const copies = callbacks.slice(0)
        callbacks.length = 0
        for (let i = 0; i < copies.length; i++) {
          copies[i]()
        }
      }
      
      在浏览器不执行MutationObserverPromise时候,会降级成setImmediate(小知识:setImmediate的性能大于setTimeout哦!我们在设置setTimeout时延迟设置为0也会等待4毫秒,setImmediate则会立即执行)

小结

nextTick 异步更新队列 处理的时候

  • 1、先将回调函数放入callbacks数组中,优先以微任务的形式执行微任务,如果浏览器不支持的话会降级成宏任务的形式