Vue生命周期概述
- 1 概述
- 2 初始阶段
- 3 挂载阶段
- 4 更新阶段
- 5 销毁阶段
- 6 总结
1 概述
每个Vue组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到DOM,以及在数据改变时更新DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。
Vue的声明周期包括:
- 初始阶段
- 挂载阶段
- 更新阶段
- 销毁阶段
下面用官网的声明周期图来解释说明:
2 初始阶段
初始阶段的生命周期有:beforeCreate
、created
,相应流程如图所示:
- 创建Vue实例之后,首先会初始化生命周期和事件,但是这时数据代理还未开始,也就是说
vm
上面还没有_data
属性,那么data
中定义的数据接收不到 - 执行
beforeCreate
生命周期函数 - 紧接着数据初始化,数据监测与数据代理。这个过程,要定义
data
数据、方法等,此时可以通过vm
访问到data
中的数据和methods
中的方法 - 执行
created
生命周期函数
下面来进行验证,首先定义如下所示HTML结构:
<div id="app">
<button @click="add">点击+1</button>
<span>{{number}}</span>
</div>
接着初始化一个Vue实例,在data
中定义number
的值,并在methods
中定义方法add
:
const vm = new Vue({
el: "#app",
data: {
number: 0 // 初始化number为0
},
methods: {
add() { // 每次点击number加1
this.number++;
}
},
})
首先验证beforeCreate
,由于这时还没有进行数据代理,因此vm
上面没有_data
和methods
中的方法,我们可以输出一个this
来查看:
beforeCreate() {
console.log(this);
}
输出结果如下,我们可以看到这时的vm
上面即没有data
,也没有methods
。
紧接着验证created
,此时进行了数据监测与数据代理,因此可以通过vm
访问到_data
和methods
中的方法,我们可以输出一个this
来查看:
created() {
console.log(this);
}
输出结果如下,这是vm
上面有了add
方法和在data
中定义的number
数据:
完整验证代码如下:
<div id="app">
<button @click="add">点击+1</button>
<span>{{number}}</span>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
number: 0
},
methods: {
add() {
this.number++;
}
},
beforeCreate() {
console.log(this);
},
created() {
console.log(this);
},
})
</script>
3 挂载阶段
挂载阶段的生命周期有:beforeMount
、mounted
在created
执行完之后,接下来会判断是否有el
的配置项,el
配置项就是我们最先开始在Vue实例中定义的el: "#app"
- 如果没有,那么会等待调用
vm.$mount(el)
方法,如果一直不调用该方法,渲染会停止;调用vm.$mount(el)
之后,才会继续判断是否有template
配置项; - 如果有,则会继续判断是否有
template
配置项,如果有该配置,编译template里面的内容;如果没有,编译el的外部html
作为模版;
上面这个阶段Vue会开始解析模版,生成虚拟DOM,但是页面还不能显示解析好的内容,这时的流程是:
- 执行
beforeMount
生命周期函数 - 接下来将虚拟DOM转化为真实DOM,并往
vm.$el
存了一份,并且将真实DOM插入到了页面上 - 执行
mounted
生命周期函数
下面来进行验证,首先验证beforeMount
,在该函数执行时,Vue生成了虚拟DOM,但是页面不能显示解析好的内容,因此我们写了什么代码,页面上就显示什么代码,并且该函数中所有对DOM的操作都不生效,使用debugger
调试如下:
beforeMount() {
document.querySelector("button").innerText = "橘猫吃不胖";
debugger;
}
结果如下,可以看到这时页面上的{{number}}
还没有被解析,并且按钮的文字被改成了橘猫吃不胖
:
但是当我们继续执行下去的时候,发现按钮的文字又变回了点击+1
,由此可以看出beforeMount
中对DOM的操作没有生效:
接着验证mounted
,在这个函数中,我们可以输出this.$el
,结果是真实DOM:
mounted() {
console.log(this.$el);
}
完整验证代码如下:
<div id="app">
<button @click="add">点击+1</button>
<br>
<span>{{number}}</span>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
number: 0
},
methods: {
add() {
this.number++;
}
},
beforeMount() {
document.querySelector("button").innerText = "橘猫吃不胖";
debugger;
},
mounted() {
console.log(this.$el);
},
})
</script>
4 更新阶段
更新阶段的生命周期有:beforeUpdate
、updated
,相应流程如图所示:
- 当状态数据发生了变化之后,会执行
beforeUpdate
生命周期函数,此时数据是最新的,但是页面是旧的 - 紧接着新旧虚拟DOM会进行对比,并完成页面的更新
- 执行
updated
生命周期函数
数据更新时,会循环执行上面的流程。
下面验证一下。首先验证beforeUpdate
,在这个函数中,数据是最新的,但是没有更新到页面上,因此我们可以通过点击按钮来更新数据,观察变化:
beforeUpdate() {
console.log(this.number);
debugger;
}
效果如下,我们可以看到点击按钮之后,这时的number
是1,但是页面上显示的还是0,因此页面尚未和数据保持同步。
如果我们在函数中同样使用以上代码,我们就可以看到更新后的数据和页面了:
updated() {
console.log(this.number);
debugger;
}
完整验证代码如下:
<div id="app">
<button @click="add">点击+1</button>
<br>
<span>{{number}}</span>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
number: 0
},
methods: {
add() {
this.number++;
}
},
beforeUpdate() {
console.log(this.number);
debugger;
},
updated() {
console.log(this.number);
debugger;
},
})
</script>
5 销毁阶段
销毁阶段的生命周期有:beforeDestroy
、destroyed
,流程如下:
- 当
vm.$destroy()
被调用时进入销毁阶段 - 执行
beforeDestroy
生命周期函数 - 移除所有的监视、子组件和事件监听器
- 执行
destroyed
生命周期函数