Vue.js生命周期详解:从创建到销毁的全过程
Vue.js生命周期详解:从创建到销毁的全过程
Vue.js的生命周期管理是其核心特性之一,理解生命周期对于开发高质量的Vue应用至关重要。本文将通过对比人的生命周期,详细解释Vue实例从创建到销毁的整个过程,并提供具体的代码示例来演示这些生命周期钩子的使用场景。
一、生命周期概述
所谓的生命周期是指:一个事物从出生到最终的死亡,整个经历的过程叫做生命周期。
例如人的生命周期:
- 出生:打疫苗
- 3岁了:上幼儿园
- 6岁了:上小学
- 12岁了:上初中
- ……
- 58岁了:退休
- ……
- 临终:遗嘱
- 死亡:火化
可以看到,在这个生命线上有很多不同的时间节点,在不同的时间节点上去做不同的事儿。
Vue的生命周期指的是:vm对象从创建到最终销毁的整个过程。
例如:
- 虚拟DOM在内存中就绪时:去调用一个a函数
- 虚拟DOM转换成真实DOM渲染到页面时:去调用一个b函数
- Vue的data发生改变时:去调用一个c函数
- ……
- Vue实例被销毁时:去调用一个x函数
在生命线上的函数叫做钩子函数,这些函数是不需要程序员手动调用的,由Vue自动调用,程序员只需要按照自己的需求写上,到了那个时间点自动就会执行。
二、掌握Vue的生命周期有什么用
研究Vue的生命周期主要是研究:在不同的时刻Vue做了哪些不同的事儿。
例如:在vm被销毁之前,需要将绑定到元素上的自定义事件全部解绑,那么这个解绑的代码就需要找一个地方写一下,写到哪里呢?你可以写到beforeDestroy()这个函数中,这个函数会被Vue自动调用,而且是在vm对象销毁前被自动调用。
像这种在不同时刻被自动调用的函数称为钩子函数。每一个钩子函数都有对应的调用时间节点。
换句话说,研究Vue的生命周期主要研究的核心是:在哪个时刻调用了哪个钩子函数。
三、Vue生命周期的4个阶段8个钩子
Vue的生命周期可以被划分为4个阶段:初始阶段、挂载阶段、更新阶段、销毁阶段。
每个阶段会调用两个钩子函数。两个钩子函数名的特点:beforeXxx()、xxxed()。
8个生命周期钩子函数分别是:
(一)初始阶段
- beforeCreate():创建前
- created():创建后
(二)挂载阶段
- beforeMount():挂载前
- mounted():挂载后
(三)更新阶段
- beforeUpdate():更新前
- updated():更新后
(四)销毁阶段
- beforeDestroy():销毁前
- destroyed():销毁后
8个钩子函数写在哪里?直接写在Vue构造函数的options对象当中。
四、生命周期示例代码
<body>
<div id="app">
<h1>{{msg}}</h1>
<h3>计数器:{{counter}}</h3>
<button @click="add">点我加1</button>
<h3 v-text="counter"></h3>
<button @click="destroy">点我销毁</button>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
msg: "Vue生命周期",
counter: 1,
},
methods: {
add() {
console.log("add....");
this.counter++;
},
destroy() {
// 销毁vm
this.$destroy();
},
},
watch: {
counter() {
console.log("counter被监视一次!");
},
},
beforeCreate() {
// 创建前
// 创建前指的是:数据代理和数据监测的创建前。
// 此时还无法访问data当中的数据。包括methods也是无法访问的。
console.log("beforeCreate", this.counter);
// 调用methods报错了,不存在。
// this.add()
},
created() {
// 创建后
// 创建后表示数据代理和数据监测创建完毕,可以访问data中的数据了。
console.log("created", this.counter);
// 可以访问methods了。
// this.add()
},
beforeMount() {
// 挂载前
console.log("beforeMount");
// debugger //断点
},
mounted() {
// 挂载后
console.log("mounted");
// 创建真实dom元素
console.log(this.$el);
console.log(this.$el instanceof HTMLElement);
},
beforeUpdate() {
// 更新前
console.log("beforeUpdate");
},
updated() {
// 更新后
console.log("updated");
},
beforeDestroy() {
// 销毁前,解绑vue
console.log("beforeDestroy");
console.log(this);
this.counter = 1000;
},
destroyed() {
// 销毁后,vm依然存在,不过是vm身上的事件,监听解绑
console.log("destroyed");
console.log(this);
},
});
</script>
</body>
五、初始阶段做了什么事儿
做了这么几件事:
- 创建Vue实例vm(此时Vue实例已经完成了创建,这是生命的起点)
- 初始化事件对象和生命周期(接产大夫正在给他洗澡)
- 调用beforeCreate()钩子函数(此时还无法通过vm去访问data对象的属性)
- 初始化数据代理和数据监测
- 调用created()钩子函数(此时数据代理和数据监测创建完毕,已经可以通过vm访问data对象的属性)
- 编译模板语句生成虚拟DOM(此时虚拟DOM已经生成,但页面上还没有渲染)
该阶段适合做什么?
- beforeCreate:可以在此时加一些loading效果,自定义一些属性放到this身上,好比全局事件总线。
- created:结束loading效果。也可以在此时发送一些网络请求,获取数据。也可以在这里添加定时器。
六、挂载阶段做了什么事儿
做了这么几件事:
- 调用beforeMount()钩子函数(此时页面还未渲染,真实DOM还未生成,此时操作数据,不会最终显示在页面中)
- 给vm追加$el属性,用它来代替el,$el代表了真实的DOM元素(此时真实DOM生成,页面渲染完成)
- 调用mounted()钩子函数
该阶段适合做什么?
- mounted:可以操作页面的DOM元素了。也有人喜欢在这个阶段请求数据。
面试题:请求数据是在哪个钩子里,原因是什么?
七、更新阶段做了什么事儿
- data发生变化(这是该阶段开始的标志)
- 调用beforeUpdate()钩子函数(此时只是内存中的数据发生变化,页面还未更新)
- 虚拟DOM重新渲染和修补
- 调用updated()钩子函数(此时页面已更新)
该阶段适合做什么?
- beforeUpdate:适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器。
- updated:页面更新后,如果想对数据做统一处理,可以在这里完成。
Vue官方的生命周期图:
八、销毁阶段做了什么事儿
做了这么几件事:
- vm.$destroy()方法被调用(这是该阶段开始的标志)
- 调用beforeDestroy()钩子函数(此时Vue实例还在。虽然vm上的监视器、vm上的子组件、vm上的自定义事件监听器还在,但是它们都已经不能用了。此时修改data也不会重新渲染页面了)
- 卸载子组件和监视器、解绑自定义事件监听器
- 调用destroyed()钩子函数(虽然destroyed翻译为已销毁,但此时Vue实例还在,空间并没有释放,只不过马上要释放了,这里的“已销毁”指的是vm对象上所有的东西都已经解绑完成了)
该阶段适合做什么?
- beforeDestroy:适合做销毁前的准备工作,和人临终前写遗嘱类似。例如:可以在这里清除定时器,解绑自定义事件,解除监听等。