Vue的生命週期鉤子

概述

若是咱們想要從父組件獲取子組件的生命週期,除了在子組件相應的生命週期內部向父組件通訊這種作法以外,還能夠使用生命週期鉤子。vue

示例代碼

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld
      @hook:beforeCreate="console('child: beforeCreate')"
      @hook:created="console('child: created')"
      @hook:beforeMount="console('child: beforeMount')"
      @hook:mounted="console('child: mounted')"
      @hook:beforeUpdate="console('child: beforeUpdate')"
      @hook:updated="console('child: updated')"
      @hook:beforeDestroy="console('child: beforeDestroy')"
      @hook:destroyed="console('child: destroyed')"
      msg="Welcome to Your Vue.js App"
    />
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  },
  methods: {
    console(str) {
      console.log(str);
    },
  },
  beforeCreate() {
    console.log('parent: beforeCreate');
  },
  created() {
    console.log('parent: created');
  },
  beforeMount() {
    console.log('parent: beforeMount');
  },
  mounted() {
    console.log('parent: mounted');
  },
  beforeUpdate() {
    console.log('parent: beforeUpdate');
  },
  updated() {
    console.log('parent: updated');
  },
  beforeDestroy() {
    console.log('parent: beforeDestroy');
  },
  destroyed() {
    console.log('parent: destroyed');
  },
}
</script>

控制檯打印以下:app

parent: beforeCreate
parent: created
parent: beforeMount
child: beforeCreate
child: created
child: beforeMount
child: mounted
parent: mounted
parent: beforeUpdate
child: beforeUpdate
child: updated
parent: updated
parent: beforeDestroy
child: beforeDestroy
child: destroyed
parent: destroyed

源碼位置

// init.js
export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++

    let startTag, endTag
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }

    // a flag to avoid this being observed
    vm._isVue = true
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    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')

    /* 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)
    }
  }
}

// lifecycle.js
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 進行觸發的,而 callHook 裏面會 emit 這個生命週期的鉤子。ide

_hasHookEvent

能夠看到,前面會判斷 _hasHookEvent ,這是幹嗎的?源碼以下:ui

// events.js
export function initEvents (vm: Component) {
  vm._events = Object.create(null)
  vm._hasHookEvent = false
  // init parent attached events
  const listeners = vm.$options._parentListeners
  if (listeners) {
    updateComponentListeners(vm, listeners)
  }
}

export function eventsMixin (Vue: Class<Component>) {
  const hookRE = /^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
  }
  // ...
}

能夠看到,在初始化事件的時候,會將 _hasHookEvent 設置爲false,而後在$on方法裏面判斷是否有 hook 鉤子,有的話就把 _hasHookEvent 設置爲 true。咱們在子組件上綁定了 hook 鉤子,因此其內部會調用這個$on方法把 _hasHookEvent 設置爲 true,從而發出生命週期鉤子事件。this

爲何會有這個設置,但又不寫到文檔裏面去

我猜這個是遺留代碼,目前已經沒什麼用了的緣由。。。。prototype

相關文章
相關標籤/搜索