1.Vue 生命周期

Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载 dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。

2.Vue 生命周期函数

Vue 的生命周期共有 8 个阶段,即创建前/后、载入前/后、更新前/后、销毁前/销毁后,并对应地有很多钩子函数,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。

3.Vue 生命周期图

Vue 官网图片

4. Vue 生命周期进程

根据上面的图来分析 Vue 生命周期的进程:

1. 实例化 Vue(组件)对象,确定 this 指向 Vue 实例

2. 初始化事件(events)和生命周期(cycle)

3. beforeCreate 函数


这一阶段,虽然 Vue 对象已经创建,但是它的属性还没有绑定,诸如 data、computed 这些属性还没有赋值。所以 $el$data 以及 message 都是 undefined。

4. 初始化注入和响应


这一阶段,Vue 对象的属性注入绑定,完成属性赋值。

5. Created 函数


这一阶段可以访问数据,但是还没有挂载 dom,不能访问到 $el。因为这个原因,此时改变数据不会触发 updated 函数。

6. 两次判断


这个阶段会进行两次判断:

  • 判断对象是否有 el 选项
    如果有则继续向下编译;如果没有则停止编译,也即停止生命周期,直到在实例上再次调用 vm.$mount(el) 才会继续向下编译
  • 判断 el 是否有 template 模板
    如果有则将该模板编译为渲染函数,最后渲染出来;如果没有则直接调用 $el 的 outerHTML 进行渲染。另外,如果 Vue 对象中有渲染函数(render function),则它会优先于 template 进行渲染。
    综上,优先级是:render function > template > outerHTML

7. beforeMount 函数


这个阶段,this.$el 有值,但是数据还没有挂载到页面上,`{{}}` 还没有被替换

8. 用 vue 对象的数据(属性)替换模板中的内容

9. mounted 函数


模板编译完成,数据挂载完毕,页面上已经能够看到正确的数据了。

10. beforeUpdate 函数


数据更新后首先触发该函数,但更新的只是数据,innerHTML 尚未更新

11. 虚拟 dom 重新渲染并打补丁

12. updated 函数


这一阶段,innerHTML 已经得到更新。

PS:这里解释一个问题。在 beforeUpdate 函数中访问 this.$elinnerHTML 时得到的是旧数据,这是正常的;但访问 this.$el 的时候,得到的却是新数据,这是因为:

  1. console.log 是异步的,我们期望打印的时候是打印那一刻的快照,但实际上它不能做到这一点,所以在后面点击展开 this.$el 的内容时,实际上只能访问到最新更新的对象;
  2. this.$el 保存的是对象的引用。

13. beforeDestroy 函数


这一阶段,实例还没有销毁,仍然完全可用

14. 销毁实例

14. destroyed 函数


实例销毁之后调用该函数,使 Vue 实例指示的所有东西解绑定,所有的监听器被移除,所有的子实例销毁。

5.通过控制台观察生命周期的过程

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script>
</head>
<body>
<div id="app">
     <p>{{ message }}</p>
</div>
<script type="text/javascript">    
    var app = new Vue({
      el: '#app',
      data: {
          message : "Vue" 
      },
      beforeCreate: function () {
         console.group('beforeCreate 创建前状态===============》');
         console.log("%c%s", "color:red" , "el     : " + this.$el); 
         console.log("%c%s", "color:red","data   : " + this.$data); 
         console.log("%c%s", "color:red","message: " + this.message);  
        },
      created: function () {
         console.group('created 创建完毕状态===============》');
         console.log("%c%s", "color:red","el     : " + this.$el); 
         console.log("%c%s", "color:red","data   : " + this.$data); 
         console.log("%c%s", "color:red","message: " + this.message); 
      },
      beforeMount: function () {
         console.group('beforeMount 挂载前状态===============》');
         console.log("%c%s", "color:red","el     : "); console.log(this.$el);	
         console.log("%c%s", "color:red","data   : " + this.$data);  
         console.log("%c%s", "color:red","message: " + this.message); 
      },
      mounted: function () {
         console.group('mounted 挂载结束状态===============》');
         console.log("%c%s", "color:red","el     : "); console.log(this.$el);	
         console.log("%c%s", "color:red","data   : " + this.$data); 
         console.log("%c%s", "color:red","message: " + this.message); 
      },
      beforeUpdate: function () {
         console.group('beforeUpdate 更新前状态===============》');
         console.log("%c%s", "color:red","el     : "); console.log(this.$el);	 
         console.log("%c%s", "color:red","el.outerHTML   : " + this.$el.outerHTML); 
         console.log("%c%s", "color:red","data   : " + this.$data); 
         console.log("%c%s", "color:red","message: " + this.message); 
      },
      updated: function () {
         console.group('updated 更新完成状态===============》');
         console.log("%c%s", "color:red","el     : "); console.log(this.$el);	
         console.log("%c%s", "color:red","el.outerHTML   : " + this.$el.outerHTML); 
         console.log("%c%s", "color:red","data   : " + this.$data); 
         console.log("%c%s", "color:red","message: " + this.message); 
      },
      beforeDestroy: function () {
         console.group('beforeDestroy 销毁前状态===============》');
         console.log("%c%s", "color:red","el     : " + this.$el);
         console.log(this.$el);    
         console.log("%c%s", "color:red","data   : " + this.$data); 
         console.log("%c%s", "color:red","message: " + this.message); 
      },
      destroyed: function () {
         console.group('destroyed 销毁完成状态===============》');
         console.log("%c%s", "color:red","el     : " + this.$el);
         console.log(this.$el);  
         console.log("%c%s", "color:red","data   : " + this.$data); 
         console.log("%c%s", "color:red","message: " + this.message);
      }
    });
</script>
</body>
</html>

第一次渲染,可以在控制台看到:

之后手动修改 message 的值为“Vue2”,可以在控制台看到:

这里注意之前提到的 log 异步的问题,看 outerHTML 可以知道 beforeUpdate 的时候视图实际上还没有重新渲染。