1. 程式人生 > >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')來執行

如果對您有幫助請點個贊,謝謝!

原文地址:https://segmentfault.com/a/1190000016906255