美文网首页
vue的生命周期

vue的生命周期

作者: nice生活 | 来源:发表于2019-08-23 13:48 被阅读0次

    生命周期就是从Vue实例创建、运行、到销毁的一个过程,在vue中提供了生命周期钩子的函数,可以在不同生命周期阶段添加自己的代码。

    实例生命周期钩子

    lifecycle.png

    上面就是官网的图片,可以看到vue有beforeCreate,Created...等8个生命周期钩子,那么在执行到这些操作时,vue做了什么事情,我们可以从源码上看

        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')
    

    beforeCreate

    在执行beforeCreate前有initLifecycle,initEvents,initRender三个函数

    initLifecycle

    export function initLifecycle (vm: Component) {
      const options = vm.$options
      // locate first non-abstract parent
      let parent = options.parent
      if (parent && !options.abstract) {
        while (parent.$options.abstract && parent.$parent) {
          parent = parent.$parent
        }
        parent.$children.push(vm)
      }
      vm.$parent = parent
      vm.$root = parent ? parent.$root : vm
      vm.$children = []
      vm.$refs = {}
      vm._watcher = null
      vm._inactive = null
      vm._directInactive = false
      vm._isMounted = false
      vm._isDestroyed = false
      vm._isBeingDestroyed = false
    }
    

    基本上就是一下vm实例属性的初始化

    initEvents

    export function initEvents (vm: Component) {
      vm._events = Object.create(null)//创建一个空对象,vm._events存放组件父组件绑定在当前组件上的事件
      vm._hasHookEvent = false //表示的是父组件有没有直接绑定钩子函数在当前组件上
      const listeners = vm.$options._parentListeners
      if (listeners) {
        updateComponentListeners(vm, listeners)//更新事件
      }
    }
    

    主要就是事件的注册

    initRender

    export function initRender (vm: Component) {
      vm._vnode = null // the root of the child tree
      vm._staticTrees = null // v-once cached trees
      const options = vm.$options
      const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
      const renderContext = parentVnode && parentVnode.context
      vm.$slots = resolveSlots(options._renderChildren, renderContext)
      vm.$scopedSlots = emptyObject
      // bind the createElement fn to this instance
      // so that we get proper render context inside it.
      // args order: tag, data, children, normalizationType, alwaysNormalize
      // internal version is used by render functions compiled from templates
      vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
      // normalization is always applied for the public version, used in
      // user-written render functions.
      vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
    
      // $attrs & $listeners are exposed for easier HOC creation.
      // they need to be reactive so that HOCs using them are always updated
      const parentData = parentVnode && parentVnode.data
    
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production') {
        defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
          !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
        }, true)
        defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
          !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
        }, true)
      } else {
        defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
        defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
      }
    }
    

    貌似是创建虚拟节点的,=-=

    可以看出在执行beforeCreate前vue只是做一些属性,事件的初始化,而data,props并没有做初始化,故而在这里是不能用this.data的

    initInjections

    export function initInjections (vm: Component) {
      const result = resolveInject(vm.$options.inject, vm)
      if (result) {
        toggleObserving(false)  //禁用观察
        Object.keys(result).forEach(key => {
          /* istanbul ignore else */
          if (process.env.NODE_ENV !== 'production') {
            defineReactive(vm, key, result[key], () => {
              warn(
                `Avoid mutating an injected value directly since the changes will be ` +
                `overwritten whenever the provided component re-renders. ` +
                `injection being mutated: "${key}"`,
                vm
              )
            })
          } else {
            defineReactive(vm, key, result[key])  // getter setter
          }
        })
        toggleObserving(true)
      }
    }
    

    初始化 initInjections inject

    initState

    export function initState (vm: Component) {
      vm._watchers = []
      const opts = vm.$options
      if (opts.props) initProps(vm, opts.props) //初始化props
      if (opts.methods) initMethods(vm, opts.methods) // 初始化methods
      if (opts.data) {                            
        initData(vm) // 
      } else {
        observe(vm._data = {}, true /* asRootData */)
      }
      if (opts.computed) initComputed(vm, opts.computed) //computed初始化
      if (opts.watch && opts.watch !== nativeWatch) {
        initWatch(vm, opts.watch)   // watch初始化
      }
    }
    

    主要props,methods, data, computed,watch初始化

    initProvide

    export function initProvide (vm: Component) {
      const provide = vm.$options.provide
      if (provide) {
        vm._provided = typeof provide === 'function'
          ? provide.call(vm)
          : provide
      }
    }
    

    provide

    可以看出在created可以获取props,methods, data,computed,watch

    export function mountComponent (
      vm: Component,
      el: ?Element,
      hydrating?: boolean
    ): Component {
      vm.$el = el
      //略
      callHook(vm, 'beforeMount')
    
      let updateComponent
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
       //略
        }
      } else {
        updateComponent = () => {
          vm._update(vm._render(), hydrating) //挂载节点
        }
      }
      new Watcher(vm, updateComponent, noop, {
        before () {
          if (vm._isMounted && !vm._isDestroyed) {
            callHook(vm, 'beforeUpdate')
          }
        }
      }, true /* isRenderWatcher */)
      hydrating = false
      if (vm.$vnode == null) {
        vm._isMounted = true
        callHook(vm, 'mounted')
      }
      return vm
    }
    

    可以知道在beforeMount里真实的dom还没挂载所以不能用this.$refs.xx获取dom,并且是最后一次改this.data而不触发updated的生命周期,除此外beforeMount 到 mounted之间会触发子组件的生命周期。

    test

    //main.vue
    <template>
      <div id="app">
        <img alt="Vue logo" src="./assets/logo.png">
        <HelloWorld :msg="msg" ref="h"/>
      </div>
    </template>
    <script>
    import HelloWorld from './components/HelloWorld.vue'
    export default {
      name: 'app',
      components: {
        HelloWorld
      },
      data() {
         return{
             msg:'Welcome'
         } 
      },
      beforeCreate() {
          console.log(0,this.msg)
      },
      created() {
          console.log(1,this.msg)
      },
      beforeMount() {
          this.msg = 'ok'
          console.log(3,this.$ref.h)
      },
      mounted() {
          this.msg = 'ff'
          console.log(4,this.$ref.h)
      }
    }
    </script>
    //HelloWorld.vue
    <template>
      <div class="hello">
          {{test}}
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      props: {
        msg: String
      },
      computed:{
           test(){
             console.log(this.msg)
             return this.msg
        }
      },
      beforeCreate() {
          console.log(2,'child created')
      }
    }
    </script>
    

    结果

    QQ图片20190823134715.png

    相关文章

      网友评论

          本文标题:vue的生命周期

          本文链接:https://www.haomeiwen.com/subject/mkvasctx.html