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

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

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

  • 相关阅读:
    BOJ 85 Three Points On A Line
    BOJ 84 Single Number
    BOJ 83 A + B Problem
    【转载】运算符优先级
    匹配体重和为特定值的人,两两成对
    The Brand New Beginning!
    【失败】制作CentOS镜像
    【制作镜像】安装VMwareTool
    部署巡检脚本
    windows server 2008镜像重启后密码变为默认密码的问题的解决方案
  • 原文地址:https://www.cnblogs.com/yangzhou33/p/13756724.html
Copyright © 2020-2023  润新知