Vue源码学习(二)——生命周期

官网对生命周期给出了一个比较完成的流程图,如下所示:

Vue源码学习(二)——生命周期

从图中我们可以看到我们的Vue创建的过程要经过以下的钩子函数:

beforeCreate => created => beforeMount => mounted
=> beforeUpdate => updated
=> beforeDestroy => destroyed

那么我们就从源码的角度来看一看吧,当我们new Vue的时候,会执行_init函数

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

init函数如下

export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    ....
    以下就是进行了生命周期
    vm._self = vm
    // 首先进行初始化生命周期的参数
    initLifecycle(vm)
    // 在初始化事件
    initEvents(vm)
    // 初始化render
    initRender(vm)
    // 开始调用beforeCreate钩子函数,和图中的流程图一样
    callHook(vm, 'beforeCreate')
    // 之后开始初始化变量等一些数据
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    // 开始调用created钩子函数
    callHook(vm, 'created')

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }

    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}

以上init函数我们已经看到了beforeCreate和created,那么callHook是怎么调用的钩子函数呢?

export function callHook (vm: Component, hook: string) {
  // #7573 disable dep collection when invoking lifecycle hooks
  pushTarget()
  // 从$options里拿到钩子函数
  const handlers = vm.$options[hook]
  if (handlers) {
    for (let i = 0, j = handlers.length; i < j; i++) {
      try {
          // 然后再调用
        handlers[i].call(vm)
      } catch (e) {
        handleError(e, vm, `${hook} hook`)
      }
    }
  }
  if (vm._hasHookEvent) {
    vm.$emit('hook:' + hook)
  }
  popTarget()
}

这边就会有几个问题:
从vm.$options[hook]中取钩子函数,那个这个钩子函数是哪来来的? 为了拿到的钩子函数是个数组?我们平时使用不都是只是写个函数吗?

我们可以看到在$options是在下面_init中进行合并的

Vue.prototype._init = function(){
    ...
    vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
      ...
}

export const LIFECYCLE_HOOKS = [
  'beforeCreate',
  'created',
  'beforeMount',
  'mounted',
  'beforeUpdate',
  'updated',
  'beforeDestroy',
  'destroyed',
  'activated',
  'deactivated',
  'errorCaptured'
]

我们可以看到钩子函数一开始就已经在vue内部已经定义好了,并且还有几个钩子函数不是实话化实例的使用执行的。而是对keep-alive组件配合使用的activated,deactivated。以及错误抛出钩子函数errorCaptured
然后再根据这些内部定义的钩子函数和传入的参数进行合并

那么为什么钩子函数是数组呢?这个其实很简单是因为vue内部也需要执行一些函数,顾把函数也放到钩子函数里。所以需要数组遍历。

所以这些所谓的钩子函数就是一个回调函数。

其余几个钩子函数也是在需要调用的时候使用callHook(vm, 'xxx')来执行

如果对您有帮助请点个赞,谢谢!

相关推荐