hookEvent of Vue

在Vue當中,hooks能夠做爲一種event,在Vue的源碼當中,稱之爲hookEvent。javascript

<Table @hook:updated="handleTableUpdated"></Table>

從一個場景出發

有一個來自第三方的複雜表格組件,表格進行數據更新的時候渲染時間須要1s,因爲渲染時間較長,爲了更好的用戶體驗,我但願在表格進行更新時顯示一個loading動畫。html

有一個粗暴的辦法是直接修改這個組件的源碼,利用beforeUpdate和updated來顯示loading,可是這種辦法很是很差java

  • 這是一個第三方的組件,做者發佈組件的時候極可能對代碼進行了壓縮與構建,使得代碼可讀性很低,直接修改打包後的代碼難度較高
  • 若是有源碼的話,能夠fork一份本身修改,不過做者不必定會發布源碼
  • 沒法享受開源社區對這個組件的升級與維護,你須要本身手動維護

總之,修改源碼這個方案可行,可是很差,不優雅。
那麼,有沒有辦法,能夠聲明式的在模板上直接給一個組件注入一個生命週期函數呢?實際上是能夠的,就是經過hookEvent。ide

生命週期函數是如何工做的

所謂生命週期函數,就是在某一個特定的時間點調用的函數,因此咱們須要關注兩件事:一、註冊,二、調用。函數

咱們首先從調用開始。測試

vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')

上面是一段Vue的源碼,能夠看出,生命週期函數是經過callHook這個函數去調用的,天然而然,咱們去看看callHook函數的代碼動畫

export function callHook (vm: Component, hook: string) {
  // #7573 disable dep collection when invoking lifecycle hooks
  pushTarget()
  const handlers = vm.$options[hook] // 選項當中的生命週期函數
  const info = `${hook} hook`
  if (handlers) {
    for (let i = 0, j = handlers.length; i < j; i++) {
      invokeWithErrorHandling(handlers[i], vm, null, vm, info)
    }
  }
  if (vm._hasHookEvent) {
    vm.$emit('hook:' + hook)
  }
  popTarget()
}

因此,callHook('created')在vm._hasHookEvent爲true的狀況下,會執行$emit('hooks:created'),也就是說,一個有着 hook: 特殊前綴的事件,會在對應的生命週期當中執行。其實到這裏,咱們已經能夠大膽推斷了:只要使用相似@hooks:created這個形式,就能夠從Vue的模板中聲明式的注入一個生命週期函數,測試一下,it works.this

<Table @hook:updated="handleTableUpdated"></Table>

hasHookEvent

以前的大膽推斷,忽略了一個條件,那就是_hasHookEvent必需爲true,接下來就去看看這個_hasHookEventprototype

const hookRE = /^hook:/ // 以hook:開頭
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
  const vm: Component = this
  if (Array.isArray(event)) {
    for (let i = 0, l = event.length; i < l; i++) {
      vm.$on(event[i], fn)
    }
  } else {
    (vm._events[event] || (vm._events[event] = [])).push(fn)
    // optimize hook:event cost by using a boolean flag marked at registration
    // instead of a hash lookup
    if (hookRE.test(event)) {
      vm._hasHookEvent = true
    }
  }
  return vm
}

當使用了$on方法監聽事件時,若是事件名以 hooks: 做爲前綴,那麼這個事件會被當作hookEvent,註冊事件回調的同時,vm._hasHookEvent會被置爲true,當使用callHook調用生命週期函數時,因爲_hasHookEvent爲true,因此會$emit('hooks:xxx'),註冊的生命週期函數就會執行。code

因此,若是你想給一個Vue組件添加生命週期函數有3個辦法:

  • 在Vue選項中添加
  • 在模板中經過@hooks:created這種形式
  • vm.$on('hooks:created', cb)或者vm.$once('hooks:created', cb)
相關文章
相關標籤/搜索