概述

Vue 中每个组件都是一个 Vue 实例,Vue 实例需要经过创建、初始化数据、编译模板、挂载 DOM、渲染、更新、渲染、卸载等一系列过程,这个过程就是 Vue 的生命周期。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

20200419120024713.png

在 Vue 生命周期中有以下钩子函数:

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed

执行顺序

创建过程

创建过程主要涉及 beforeCreatecreatedbeforeMountmounted 四个钩子函数

执行顺序依次是:

  • Parent beforeCreate
  • Parent Created
  • Parent BeforeMount
  • Child BeforeCreate
  • Child Created
  • Child BeforeMount
  • Child Mounted
  • Parent Mounted

更新过程

更新过程主要涉及 beforeUpdateupdated 两个钩子函数,当父子组件有数据传递时才会有生命周期的比较

执行顺序依次是:

  • Parent BeforeUpdat
  • Child BeforeUpdate
  • Child Updated
  • Parent Updated

销毁过程

销毁过程主要涉及 beforeDestroydestroyed 两个钩子函数,本例直接调用 vm.$destroy() 销毁整个实例以达到销毁父子组件的目的

执行顺序依次是:

  • Parent BeforeDestroy
  • Child BeforeDestroy
  • Child Destroyed
  • Parent Destroyed

示例

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Vue父子组件生命周期</title>
  5. </head>
  6. <body>
  7. <div id="app">
  8. <p>{{message}}</p>
  9. <child :count="count"></child>
  10. <button @click="count++">++</button>
  11. </div>
  12. </body>
  13. <script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
  14. <script type="text/javascript">
  15. var child = {
  16. props: {
  17. count: {
  18. type: Number,
  19. default: 0,
  20. },
  21. },
  22. beforeCreate: function () {
  23. console.log("Child", "BeforeCreate");
  24. },
  25. created: function () {
  26. console.log("Child", "Created");
  27. },
  28. beforeMount: function () {
  29. console.log("Child", "BeforeMount");
  30. },
  31. mounted: function () {
  32. console.log("Child", "Mounted");
  33. },
  34. beforeUpdate: function () {
  35. console.log("Child", "BeforeUpdate");
  36. },
  37. updated: function () {
  38. console.log("Child", "Updated");
  39. },
  40. beforeDestroy: function () {
  41. console.log("Child", "BeforeDestroy");
  42. },
  43. destroyed: function () {
  44. console.log("Child", "Destroyed");
  45. },
  46. template: `
  47. <div>
  48. <div>{{count}}</div>
  49. </div>
  50. `,
  51. };
  52. var vm = new Vue({
  53. el: "#app",
  54. data: function () {
  55. return {
  56. count: 1,
  57. message: "father",
  58. };
  59. },
  60. components: {
  61. child,
  62. },
  63. beforeCreate: function () {
  64. console.log("Parent", "BeforeCreate");
  65. },
  66. created: function () {
  67. console.log("Parent", "Created");
  68. },
  69. beforeMount: function () {
  70. console.log("Parent", "BeforeMount");
  71. },
  72. mounted: function () {
  73. console.log("Parent", "Mounted");
  74. },
  75. beforeUpdate: function () {
  76. console.log("Parent", "BeforeUpdate");
  77. },
  78. updated: function () {
  79. console.log("Parent", "Updated");
  80. },
  81. beforeDestroy: function () {
  82. console.log("Parent", "BeforeDestroy");
  83. },
  84. destroyed: function () {
  85. console.log("Parent", "Destroyed");
  86. },
  87. });
  88. </script>
  89. </html>

加载时

image.png

更新时

image.png

销毁时

image.png

生命周期

给上面的示例加上 debugger

<!DOCTYPE html>
<html>
  <head>
    <title>Vue父子组件生命周期</title>
  </head>

  <body>
    <div id="app">
      <p>{{message}}</p>
      <child :count="count"></child>
      <button @click="count++">++</button>
    </div>
  </body>
  <script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
  <script type="text/javascript">
    var child = {
      props: {
        count: {
          type: Number,
          default: 0,
        },
      },
      beforeCreate: function () {
        debugger;
        console.log("Child", "BeforeCreate");
      },
      created: function () {
        debugger;
        console.log("Child", "Created");
      },
      beforeMount: function () {
        debugger;
        console.log("Child", "BeforeMount");
      },
      mounted: function () {
        debugger;
        console.log("Child", "Mounted");
      },
      beforeUpdate: function () {
        debugger;
        console.log("Child", "BeforeUpdate");
      },
      updated: function () {
        debugger;
        console.log("Child", "Updated");
      },
      beforeDestroy: function () {
        debugger;
        console.log("Child", "BeforeDestroy");
      },
      destroyed: function () {
        debugger;
        console.log("Child", "Destroyed");
      },
      template: `
            <div>
              <div>{{count}}</div>
            </div>
        `,
    };

    var vm = new Vue({
      el: "#app",
      data: function () {
        return {
          count: 1,
          message: "father",
        };
      },
      components: {
        child,
      },
      beforeCreate: function () {
        debugger;
        console.log("Parent", "BeforeCreate");
      },
      created: function () {
        debugger;
        console.log("Parent", "Created");
      },
      beforeMount: function () {
        debugger;
        console.log("Parent", "BeforeMount");
      },
      mounted: function () {
        debugger;
        console.log("Parent", "Mounted");
      },
      beforeUpdate: function () {
        debugger;
        console.log("Parent", "BeforeUpdate");
      },
      updated: function () {
        debugger;
        console.log("Parent", "Updated");
      },
      beforeDestroy: function () {
        debugger;
        console.log("Parent", "BeforeDestroy");
      },
      destroyed: function () {
        debugger;
        console.log("Parent", "Destroyed");
      },
    });
  </script>
</html>

beforeCreate

Vue 实例开始创建到 beforeCreate 钩子执行的过程中主要进行了一些初始化操作,初始化了一个空的 Vue 实例对象,在这个对象上只有一些默认的生命周期函数和默认事件,除此之外其它都未创建。

beforeCreate 生命周期钩子执行时组件并未挂载,datamethods 等也并未绑定,此时主要可以用来加载一些与 Vue 数据无关的操作,例如展示一个 loading 等。

image.png

console.log(this.$el);       // undefined
console.log(this.$data);     // undefined 
console.log(this.message);   // undefined

created

beforeCreatecreated 的过程中主要完成了数据绑定的配置、计算属性与方法的挂载、watch/event 事件回调等。

created 生命周期钩子执行时组件未挂载到到 DOM,属性 $el 目前仍然为 undefined,但此时已经可以开始操作 datamethods 等,只是页面还未渲染,在此阶段通常用来发起一个 XHR 请求。

image.png

console.log(this.$el);      // undefined
console.log(this.$data);    // {__ob__: Observer} 
console.log(this.message);  // father

beforeMount

createdbeforeMount 的过程中主要完成了页面模板的解析,首先会判断对象是否有 el 选项。如果有的话就继续向下编译,如果没有,则停止编译,也就意味着停止了生命周期,直到在该 vue 实例上调用 vm.$mount(el)

beforeMount 生命周期钩子执行时 $el 被创建,但是页面只是在内存中,并未作为 DOM 渲染。

image.png

在父组件执行 beforeMount 阶段后,进入子组件的 beforeCreatecreatedbeforeMount阶段,这些阶段和父组件类似,按下不表。beforeMount 阶段后,执行的是 Mounted 阶段,该阶段时子组件已经挂载到父组件上,并且父组件随之挂载到页面中。

console.log(this.$el);      // <div id="app">...</div>,只存在内存中
console.log(this.$data);    // {__ob__: Observer}
console.log(this.message);  // father

mounted

beforeMountmounted 的过程中执行的是将页面从内存中渲染到 DOM 。此时可以执行依赖 DOM 的操作

mount 生命周期钩子执行时页面已经渲染完成,组件正式完成创建阶段的最后一个钩子,即将进入运行中阶段。此外关于渲染的页面模板的优先级,是 render 函数 > template 属性 > 外部 HTML。

image.png

console.log(this.$el);      // <div id="app">...</div>,已经渲染完成
console.log(this.$data);    // {__ob__: Observer} 
console.log(this.message);  // father

beforeUpdate

当数据发生更新时 beforeUpdate 钩子便会被调用,此时 Vue 实例中数据已经是最新的,但是在页面中的数据还是旧的,在此时可以进一步地更改状态,这不会触发附加的重渲染过程。

image.png

在父组件执行 beforeUpdate 阶段后,进入子组件的 beforeUpdateupdated 阶段,当执行完子组件的 beforeUpdate 阶段时,页面会发生渲染

image.png

console.log(this.$el);    // <div id="app">...</div>
console.log(this.$data);  // {__ob__: Observer} 

// parent BeforeUpdate
console.log(this.count);  // 2,此时并没有渲染

// Child BeforeUpdate
console.log(this.count);  // 2,此时已经渲染在页面上了

updated

当数据发生更新并在 DOM 渲染完成后 updated 钩子便会被调用,在此时页面的数据和 data 的数据已经保持同步了,可以执行依赖于 DOM 的操作。

image.png

console.log(this.$el);    // <div id="app">...</div>
console.log(this.$data);  // {__ob__: Observer} 
console.log(this.count);  // 2

beforeDestroy

Vue 实例被销毁之前 beforeDestroy 钩子便会被调用,在此时实例仍然完全可用。

image.png

在父组件执行 beforeDestroy 阶段后,进入子组件的 beforeDestroydestroy 阶段,这些阶段和父组件类似,按下不表。beforeDestroy 阶段后,执行的是 destroy 阶段

vm.$destroy();
console.log(this.$el);      // <div id="app">...</div>
console.log(this.$data);    // {__ob__: Observer} 
console.log(this.message);  // father

destroyed

Vue 实例被销毁之后 destroyed 钩子便会被调用,在此时 Vue 实例绑定的所有东西都会解除绑定,所有的事件监听器会被移除,所有的子实例也会被销毁,组件无法使用,datamethods 也都不可使用,即使更改了实例的属性,页面的 DOM 也不会重新渲染。

image.png

console.log(this.$el);      // <div id="app">...</div>
console.log(this.$data);    // {__ob__: Observer} 
console.log(this.message);  // father