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>

控制台打印如下:

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 这个生命周期的钩子。

_hasHookEvent

可以看到,前面会判断 _hasHookEvent ,这是干嘛的?源码如下:

// 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,从而发出生命周期钩子事件。

为什么会有这个设置,但又不写到文档里面去

我猜这个是遗留代码,目前已经没什么用了的原因。。。。

原文地址:https://www.cnblogs.com/yangzhou33/p/13756724.html