Vue生命周期详解。

Vue 生命周期


vue 父子组件渲染生命周期执行循序

Vue 声明周期表格

一说到 Vue 的生命周期,大家都能说上来一点.我自己会随着自己对 Vue 的深入了解,不断的完善自己对 Vue 的整体认识。
本文 Vue 的版本 Vue 2.x 官方生命周期图解:

vue-hook

生命周期钩子 详细
beforeCreate 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
created 在实例创建完成后被立即调用。在这一步,实例已经完成以下配置:数据观测(data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没有开始,\$el属性目前不可见。
beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。
mounted el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。
注意 mounted 不会 承诺所有的子组件也一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.\$nextTick 替换掉 mounted。 该钩子在服务器端渲染期间不被调用。
beforeUpdate 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现在的 DOM,比如手动移除已添加的事件监听器。该钩子在服务器渲染期间不被调用,因为只有初次渲染会在服务器端运行
updated 由于数据更改导致的虚拟 dom 重新渲染和打补丁,在这之后会调用该钩子。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然后在大多数情况下,你应该避免在此期间更改状态。如果要相应的状态改变,通常最好使用 计算属性watcher取而代之。
注意 updated 不会承诺所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以用 vm.\$nextTick 替换掉 updated
beforeDestory 实例销毁之前调用。在这一步,实例仍然完全可用。 该钩子在服务器端渲染期间不被调用。
destoryed Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<div id="app">
<p @click="setMessage">{{message}}</p>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Vue的生命周期'
},
methods: {
setMessage: function () {
this.message = 'updateMessage';
}
},
beforeCreate() {
console.group('------beforeCreate创建前状态------');
console.log('%c%s', 'color:red', this.$el); // undefined
console.log('%c%s', 'color:red', this.$data); // undefined
console.log('%c%s', 'color:red', this.message); // undefined
},
created() {
console.group('------created创建完毕状态------');
console.log('%c%s', 'color:red', this.$el); // undefined
console.log('%c%s', 'color:red', this.$data); // Object ** 已经初始化
console.log('%c%s', 'color:red', this.message); // Vue的生命周期 ** 已经初始化
},
// 当不存在el的时候只会执行到前面两个生命周期
beforeMount() {
console.group('------beforeMount挂载前状态------');
console.log('%c%s', 'color:red', this.$el); // 已经初始化
console.log(this.$el);
console.log('%c%s', 'color:red', this.$data); // 已经初始化
console.log('%c%s', 'color:red', this.message); // 已经初始化
},
mounted() {
console.group('------mounted挂载前状态------');
console.log('%c%s', 'color:red', this.$el); // 已经初始化
console.log(this.$el);
console.log('%c%s', 'color:red', this.$data); // 已经初始化
console.log('%c%s', 'color:red', this.message); // 已经初始化
},
beforeUpdate() {
console.group('------beforeUpdate挂载前状态------');
console.log('%c%s', 'color:red', this.$el);
console.log(this.$el);
console.log('%c%s', 'color:red', this.$data);
console.log('%c%s', 'color:red', this.message);
},
updated() {
console.group('------updated挂载前状态------');
console.log('%c%s', 'color:red', this.$el);
console.log(this.$el);
console.log('%c%s', 'color:red', this.$data);
console.log('%c%s', 'color:red', this.message);
},
beforeDestroy() {
console.group('------beforeDestroy挂载前状态------');
console.log('%c%s', 'color:red', this.$el);
console.log(this.$el);
console.log('%c%s', 'color:red', this.$data);
console.log('%c%s', 'color:red', this.message);
},
destroyed() {
console.group('------destroyed挂载前状态------');
console.log('%c%s', 'color:red', this.$el);
console.log(this.$el);
console.log('%c%s', 'color:red', this.$data);
console.log('%c%s', 'color:red', this.message);
}
});
</script>
</body>
</html>

注意事项

1、beforeMount 生命周期之前

vue-hook
在这个调用beforeMount判断vm.$options是否存在el,如果没有的话则会停止编译,也就意味着停止了生命周期,直到在改vue实例上调用vm.$mount(el)

vue 的实现代码:如果存在el

1
2
3
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}

原型上的$mount实现

1
2
3
4
5
6
7
8
9
10
11
Vue.prototype.$mount = function (el, hydrating) {
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating);
};
function mountComponent(vm, el, hydrating) {
vm.$el = el;
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode;
}
callHook(vm, 'beforeMount');
}

如果我们注释掉上面el属性的代码,并且不再后续调用vm.$mount(el)

1
// el: '#app'

如果没有el他只会执行前两个生命周期beforeCreate、created,如果我们在后续调用了vm.\$mount(el),可以发现代码继续向下执行了

在浏览器上调用:

1
vm.$mount('#app');

他就会接着执行下去

2、template 属性、outer html、render 函数三个优先级

  1. 如果直接调用render函数,那么他的优先级最高。
  2. 如果vue实例对象中有template参数选项,则将其作为模板编译成render函数。
  3. 如果没有template选项,则将外部HTML作为模板编译。
  4. 可以看到template中的模板优先级要高于outer HTML的优先级。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<div id="app">
// 如果render、template都不存在 会显示这个
<p>{{message + '这是在outer HTML中的'}}</p>
// 如果三个都存在 最终会显示
<h1>this is createElement</h1>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
template: "<h1>{{message +'这是在template中的'}}</h1>", // 如果render不存在 只有 template 会显示这个
data: {
message: 'Vue的生命周期'
},
render: function (createElement) {
return createElement('h1', 'this is createElement');
}
});
</script>
</body>
</html>

3、mounted
可以看到此时是给vue实例对象添加$el成员,并且替换掉挂在的DOM元素。因为在之前console中打印的结果可以看到beforeMount之前el上还是undefined

4、mounted
mounted之前h1中还是通过进行占位的,因为此时还有挂在到页面上,还是JavaScript中的虚拟DOM形式存在的。在mounted之后可以看到h1中的内容发生了变化。

5、beforeUpdate 和 updated
vue发现data中的数据发生了改变,会触发对应组件的重新渲染,先后调用beforeUpdateupdated钩子函数。我们点击当前的p标签就会触发data中的message更新

6、beforeDestroy 和 destroyed
beforeDestroy 钩子函数在实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed 钩子函数在Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

参考

详解 vue 生命周期
详解 Vue 生命周期实现