写作不易,Star是最大鼓励,感觉写的不错的可以给个Star⭐,请多多指教。本博客的Github地址。
[TOC] Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是Vue的生命周期。
生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件统称为生命周期钩子。生命周期钩子其实就是生命周期事件的别名而已。 生命周期钩子 = 生命周期函数 = 生命周期事件
Vue生命周期总共分为8个阶段: 创建(create)前/后,载入(mount)前/后,更新(update)前/后,销毁(destroy)前/后。
生命周期函数分类
<div id="app">
<h2 id="h2">{{msg}}</h2>
<button @click="change">变更</button>
</div>
2
3
4
<script>
let vm = new Vue({
el: '#app',
data: {
msg: 'test'
},
methods: {
change() {
this.msg = 'test-change';
}
},
beforeCreate() {
// console.log(this.msg); // undefined
// this.change(); // Error in beforeCreate hook: "TypeError: this.change is not a function"
},
created() {
// console.log(this.msg); // test
// this.change();
// console.log(this.msg); // test-change
},
// beforeMount() {
// console.log(document.querySelector('#h2').innerText); // {{msg}}
// },
// mounted() {
// console.log(document.querySelector('#h2').innerText); // test
// },
beforeUpdate() {
console.log('beforeUpdate-界面上元素的内容:' + document.getElementById('h2').innerText); // test
console.log('beforeUpdate-data中的msg数据是:' + this.msg); // test-change
},
updated() {
console.log('界面上元素的内容:' + document.getElementById('h2').innerText); // test-change
console.log('data中的msg数据是:' + this.msg); // test-change
}
});
</script>
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
创建期间的生命周期函数
- beforeCreate(创建前):Vue实例刚刚在内存中被创建出来,此时,Vue实例的挂载元素$el和数据对象data都是undefined,data和methods属性也还没有初始化好;
// 无法访问data中的数据和methods中的方法
beforeCreate() {
console.log(this.msg); // undefined
this.change(); // Error in beforeCreate hook: "TypeError: this.change is not a function"
}
2
3
4
5
- created(创建后):实例已经在内存中创建好了,此时data和methods也都已经创建好了,此时还没有开始编译模板,$el还未初始化;
// 可以访问data中的数据和methods中的方法
created() {
console.log(this.msg); // test
this.change();
console.log(this.msg); // test-change
}
2
3
4
5
6
- beforeMount(载入前):Vue实例的$el和data都初始化了,相关的render函数首次被调用。实例已完成以下的配置:编译模板(内存中编译的虚拟DOM),把data里面的数据和模板生成html。注意:此时还没有挂载html到页面上。
beforeMount() {
console.log(document.querySelector('#h2').innerText); // {{msg}}
}
2
3
- mounted(载入后):el被新创建的
vm.$el
替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中,在该生命周期中进行ajax交互。
mounted() {
console.log(document.querySelector('#h2').innerText); // test
}
2
3
运行期间的生命周期函数
- beforeUpdate(更新前):在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。此时,data中的状态值是最新的,但是界面上显示的数据还是旧的,因为此时还没有开始重新渲染DOM节点。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
// 当data中的数据发生变化的时候,该生命周期中页面上的内容还未更新
beforeUpdate() {
console.log('beforeUpdate-界面上元素的内容:' + document.getElementById('h2').innerText); // test
console.log('beforeUpdate-data中的msg数据是:' + this.msg); // test-change
}
2
3
4
5
- updated(更新后):实例更新完毕之后调用此函数,此时data中的状态值和界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了。由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
// 当data中的数据发生变化的时候,该生命周期中页面上的内容发生更新
updated() {
console.log('界面上元素的内容:' + document.getElementById('h2').innerText); // test-change
console.log('data中的msg数据是:' + this.msg); // test-change
}
2
3
4
5
销毁期间的生命周期函数
- beforeDestroy(销毁前):在实例销毁之前调用。在这一步,实例仍然完全可用。
- destroyed(销毁后):Vue实例销毁之后调用。调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
生命周期汇总
生命周期示意图
父组件和子组件生命周期钩子函数执行顺序
Vue的父组件和子组件生命周期钩子函数执行顺序可以归类为以下4部分:
加载渲染过程
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
子组件更新过程
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
父组件更新过程
父beforeUpdate -> 父updated
销毁过程
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
在哪个生命周期中调用异步请求
可以在钩子函数created、beforeMount、mounted中进行调用,因为在这三个钩子函数中,data已经创建,可以将服务端返回的数据进行赋值。但是,推荐在created钩子函数中调用异步请求,因为在created钩子函数中调用异步请求有以下优点:
- 能更快获取到服务端数据,减少页面loading时间;
- ssr不支持beforeMount、mounted钩子函数,所以放在created中有助于一致性;
在哪个阶段才能访问和操作DOM
在钩子函数mounted被调用前,Vue已经将编译好的模板挂载到页面上,所以在mounted中可以访问操作DOM。
父组件可以监听到子组件的生命周期吗
比如有父组件Parent和子组件Child,如果父组件监听到子组件挂载mounted就做一些逻辑处理,可以通过以下写法实现:
// Parent.vue
<Child @mounted="doSomething" />
// Child.vue
mounted() {
this.$emit("mounted");
}
2
3
4
5
6
7
以上需要手动通过$emit触发父组件的事件,更简单的方式可以在父组件引用子组件时通过@hook来监听即可,如下所示:
// Parent.vue
<Child @hook:mounted="doSomething" ></Child>
doSomething() {
console.log('父组件监听到mounted钩子函数 ...');
},
// Child.vue
mounted() {
console.log('子组件触发mounted钩子函数...');
},
// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
当然@hook方法不仅仅是可以监听mounted,其它的生命周期事件,例如:created,updated等都可以监听。