1. 前言

 对于Vue的生命周期的理解,刚开始接触Vue时,Vue官网告诉我即使你先不太理解Vue的生命周期也能学好Vue,但是实际上是在实际开发过程中会因为对生命周期的理解浅薄而猜很多坑,所以我在经历过很多项目后,又重新学习了Vue生命周期,与各位共勉。

一、vue的生命周期函数主要分类:
  1. 创建期间的生命周期方法
    • beforeCreate(){},
    • created(){},
    • beforeMount(){}
    • mounted(){},
  2. 运行期间的生命周期方法
    • beforeUpdate(){},
    • updated(){},
  3. 销毁期间的生命周期方法
    • beforeDestroy(){},
    • destroyed(){}
二、本文将从以下几点进行理解Vue的生命周期:
  1. 在new Vue({})之后,Vue的内部做了什么操作?
  2. Vue的生命周期定义?
  3. Vue的生命周期在实际项目中的执行顺讯是什么?
  4. Vue的内置函数、属性和Vue的生命周期的运行顺序?

2.Vue内部回调

 在我们创建Vue之后,Vue内部其实做了很多操作,如下所示会将下面的参数传递给Vue内部,

const app=new Vue({
    el:"#app", 
    props:{},
    methods:{},
    data:{},
    computed:{},
    watch:{},
    beforeCreate(){},
    created(){},
    beforeMount(){}
    mounted(){},
    beforeUpdate(){},
    updated(){},
    beforeDestroy(){},
    destroyed(){}
})

 例如以上的生命周期函数传递给Vue内部之后,Vue内部会进行回调,现在我们就进入Vue源码来追踪一下回调过程。

  1. 打开vue-2.6.14->src->core->index.js
  2. 在index.js中发现Vue对象从’./instance/index.js’导入的。
  3. 进入’./instance/index.js’,我们可以发现对Vue的定义
    function Vue (options) {
      if (process.env.NODE_ENV !== 'production' &&
        !(this instanceof Vue)
      ) {
        warn('Vue is a constructor and should be called with the `new` keyword')
      }
      this._init(options)
    }
    这里的this._init(options)函数中的options就是在创建new Vue({})时携带的内置函数、属性和Vue的生命周期函数等参数
  4. 进入’./init.js’
    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')
    如上所示callHook(vm, ‘beforeCreate’)和 callHook(vm, ‘created’),这里指在创建Vue之后会自动回调这两个函数

    3.Vue的生命周期定义

     生命周期函数又叫做钩子函数,vue每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁,这就是一个组件所谓的生命周期。在组件中具体的方法有:
    beforeCreate(){},
    created(){},
    beforeMount(){}
    mounted(){},
    beforeUpdate(){},
    updated(){},
    beforeDestroy(){},
    destroyed(){}

4.Vue的生命周期在实际项目中的执行顺讯是什么

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <div id="app">
      <button @click="changeRender">更改数据</button>
      <h1>{{rendered}}</h1>
    </div>
    <script src="../../../../Vue/vue.js"></script>
    <script type="text/javascript">
      const app = new Vue({
        el: '#app',
        props: {},
        methods: {
          changeRender(){
            this.rendered = true
          },
          callRender(){
            console.log('Render is called')
          }
        },
        data() {
          return {
            rendered: false,
          }
        },
        computed: {},
        watch: {},
        beforeCreate() {
          /*
          在调用beforeCreate的时候,仅仅表示Vue实例刚刚被创建出来,
          此时此刻还没有初始化好Vue实例中的数据和方法,
            Dom:不可访问
            Vue实例保存的数据:不可访问
            Vue实例保存方法以:不可访问
          */

          console.log('-------------------beforeCreate start-------------------------------');
          console.log('beforeCreate is called '+ this.$el);  //undefined
          console.log('beforeCreate is called '+ this.rendered); // undefined 
          // this.callRender() //报错访问不到
          console.log('-------------------beforeCreate end-------------------------------');
        },
        created() {
          /*
          在调用created的时候,是我们最早访问Vue实例中保存的数据和方法的地方。
            Dom:不可访问
            Vue实例保存的数据:可以访问
            Vue实例保存方法以:可以访问
          */
          console.log('-------------------created start-------------------------------');
          console.log('created is called '+ this.$el);//undefined
          console.log('created is called '+ this.rendered);   //false
          this.callRender() //Render is called
          console.log('-------------------created end-------------------------------');

        },

        beforeMount() {
        /*
          Dom:已经编译好最终模版,但是不会在页面上渲染
              在调用beforeMount的时候,表示Vue已经编译好最终模版,但是还没有将最终模版渲染到界面。
              Dom:已经编译好最终模版,但是还没有将最终模版渲染到界面
                <div id="app">
                  <button @click="changeRender">更改数据</button>
                  <h1>{{rendered}}</h1>
                </div>
            Vue实例保存的数据:可以访问
            Vue实例保存方法以:可以访问
          */
          console.log('-------------------beforeMount start-------------------------------');
          console.log(this.$el);//输出div#app
          console.log('beforeMount is called '+ this.rendered); //false
          this.callRender() //Render is called
          console.log('-------------------beforeMount end-------------------------------');

        },
        mounted() {
        /*
          在调用mounted的时候,表示Vue已经完成了模版的渲染,表示我们可以拿到界面上渲染之后的内容了
            Dom:已经完成了模版的渲染
            <div id="app">
              <button>更改数据</button> 
            <h1>false</h1></div>
            Vue实例保存的数据:可以访问
            Vue实例保存方法以:可以访问
          */
          console.log('-------------------mounted start-------------------------------');
          console.log(this.$el);//输出div#app
          console.log('mounted is called '+ this.rendered);  //false
          this.callRender() //Render is called
          console.log('-------------------mounted end-------------------------------');

        },
        beforeUpdate() {
        /*
          1、在调用changeRender的时候,表示Vue中保存的实例被修改了,因为会触发beforeUpdate
          2、在调用beforeUpdate的时候,数据已经更新了,但是界面没有更新
            Dom:数据已经更新了,但是界面没有更新
              <div id="app">
                <button>更改数据</button> 
              <h1>false</h1></div>
            Vue实例保存的数据:可以访问
            Vue实例保存方法以:不可以访问
          */
          console.log('-------------------beforeUpdate start-------------------------------');
          console.log( this.$el);//输出div#app
          console.log('beforeUpdate is called '+ this.rendered); //true
          this.callRender()//Render is called
          console.log('-------------------beforeUpdate end-------------------------------');
        },
        updated() {
          /*
          1、在调用changeRender的时候,表示Vue中保存的实例被修改了,因为会触发beforeUpdate
          2、在调用updated的时候,数据已经更新,且界面也更新了。
            Dom:数据已经更新,且界面也更新了。
              <div id="app">
                <button>更改数据</button> 
              <h1>true</h1></div>
            Vue实例保存的数据:可以访问
            Vue实例保存方法以:不可以访问
          */
          console.log('-------------------updated start-------------------------------');
          console.log('updated is called '+ this.$el);//输出[object HTMLDivElement]
          console.log('updated is called '+ this.rendered)//true
          this.callRender() //Render is called
          console.log('-------------------updated end-------------------------------');
        },

        beforeDestroy() {
          /*
            在调用beforeDestroy的时候,表示当前实例或者组件即将被销毁了,
            注意点:只要组件不被销毁,那么beforeDestroy就不会被调用
                  beforeDestroy是可以访问组件或者实例的数据和方法
          */
          console.log('-------------------beforeDestroy start-------------------------------');
          console.log(this.$el);//输出div#app
          console.log(this.rendered);//true
          this.callRender()
          console.log('-------------------beforeDestroy end-------------------------------');

        },
        destroyed() {
          /*
            在调用destroyed的时候表示当前组件已经被销毁了。
            注意点:只要组件不被销毁,该函数就不会被调用
                不要再这个生命周期方法里才做数据和方法
          */
          console.log('-------------------destroyed start-------------------------------');
          console.log(this.$el);//输出div#app
          console.log(this.rendered);
          this.callRender()
          console.log('-------------------destroyed end-------------------------------');
        }
      })
    </script>
  </body>
</html>

5.vue中内置的方法、属性和vue生命周期的运行顺序

data的初始化在create时已经完成了,并且methods、computed、props等也初始化完毕。
如下Vue部分源码:

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

因此props -> methods -> data -> computed -> watch

评 论