Vue 的生命周期描述组件从创建到销毁的全过程。Vue3 和 Vue2 的生命周期钩子非常像,我们仍然可以在相同的场景下使用相同的钩子函数。
Vue3 在设计时对先前的版本进行了向下兼容,如果你的项目还在使用选项式 API 进行构建,那么不需要修改生命周期相关的代码。如果使用组合式 API 进行项目构建,生命周期钩子函数会有略微不一样。
一、Vue 的生命周期
下图为 Vue 的生命周期,包括 Vue2 和 Vue3:
一个 Vue 组件的生命周期分为4个阶段,组件创建阶段、组件挂载阶段、数据更新阶段和组件销毁阶段,对应8个钩子函数。
1. Vue3 和 Vue2 生命周期的不同
Vue3 的生命周期和 Vue2 相比有2点不同:
(1) 组件实例在创建之前和创建之后这两个钩子,在 Vue2 中分为 beforeCreate 和 created 两个不同的钩子函数;在 Vue3 中都统一叫做 setup。
(2) 生命周期钩子函数命名有所不同。组件在挂载之前,Vue2 中叫做 beforeMount,Vue3 中叫做 onBeforeMount,在前面加了个 on。这样能够让生命周期钩子函数更加规范,就像一个事件一样。
2. Vue3 生命周期
除了 beforeCreate 和 created(被 setup 方法本身代替),共有9个 Options API 生命周期相对应的方法可以在 setup 中使用。
- onBeforeMount——挂载开始前调用
- onMounted——挂载后调用
- onBeforeUpdate——当响应数据改变,且重新渲染前调用
- onUpdated——重新渲染后调用
- onBeforeUnmount——Vue 实例销毁前调用
- onUnmounted——实例销毁后调用
- onActivated——当 keep-alive 组件被激活时调用
- onDeactivated——当 keep-alive 组件取消激活时调用
- onErrorCaptured——从子组件中捕获错误时调用
前6个钩子函数在上图中都可以见到,剩下3个会在对应的情况发生时调用。
3. 使用生命周期钩子函数
Vue2 中生命周期钩子函数都暴露在组件实例中,使用生命周期钩子函数只需要按照对应的规则,在 Vue 实例上编写响应代码即可。如下所示:
<script>
export default {
mounted() {
console.log('mounted');
},
updated() {
console.log('updated');
}
};
</script>
在 Vue3 中,生命周期钩子函数都分别是一个独立的模块,使用生命周期函数需要在 setup 中进行使用,需要先进行引入。代码如下:
<script>
import { onMounted } from "vue";
export default defineComponent({
setup() {
onMounted(() => {
console.log('onMounted')
});
}
}
</script>
二、各个生命周期钩子函数使用场景
1. beforeCreate
export default {
data() {
return {
val: 'hello'
}
},
beforeCreate() {
console.log('value of val is ' + this.val); // value of val is undefined
}
}
beforeCreate 对那些不需要分配数据的逻辑和 API 调用来说十分有用。如果此时对数据对象赋值,那么这些值会在状态初始化后丢失。
2. created
export default {
data() {
return {
val: 'hello'
}
},
beforeCreate() {
console.log('value of val is ' + this.val); // value of val is hello
}
}
created 对需要处理响应式数据读/写时非常有用。举个例子,如果你需要完成一个 API 调用并存储它的值,那么你应该将它写在这里。
3. beforeMount 和 onBeforeMount
在组件 dom 实际渲染和挂载前触发。
在此阶段,挂载元素(root element)还未存在。
在选项式 API 中,可以通过 this.$el 来访问挂载元素。而在组合式 API 中,必须通过 ref 来访问挂载元素。
<template>
<div ref="root">Hello Root</div>
</template>
<script>
import { ref, onBeforeMount} from "vue";
export default {
setup() {
const root = ref(null);
onBeforeMount(() => {
console.log(root.value); // null
});
return {
root
}
}
}
</script>
4. mount 和 onMounted
在组件 dom 实际渲染和挂载后触发。
<template>
<div ref="root">Hello Root</div>
</template>
<script>
import { ref, onMounted } from "vue";
export default {
setup() {
const root = ref(null);
onMounted(() => {
console.log(root.value); // <div>Hello Root</div>
});
return {
root
}
}
}
</script>
5. beforeUpdate 和 onBeforeUpdate
在数据修改且组件重新渲染前执行,这是手动修改 dom 的好地方。
6. updated 和 onUpdated
在数据修改且组件重新渲染后执行。
import { ref, onBeforeUpdate, onUpdated} from "vue";
export default {
setup() {
const count = ref(0);
const val = ref(0);
onBeforeUpdate(() => {
count.value++;
console.log('beforeUpdate');
});
onUpdated(() => {
console.log('updated() val ' + val.value);
});
return {
count,
val
}
}
}
这些钩子函数很有用,但多数情况我们可能会通过监听器(watchers)去检测对应数据的改变。
因为监听器在副作用函数中提供数据更改时的旧值和新值。
这里简单介绍一下副作用函数。当我们改变一个数据时,原本的行为我们想让更新页面的 UI。现在我们想让它除了更新页面,再触发一个额外的操作,比如发起网络请求、拉取另外的数据等,这就叫做副作用。简而言之,就是我们想让它去做它原本不会去做的事情。
7. beforeUnmount 和 onBeforeUnmount
在组件销毁之前触发,在此会进行绝大多数的清理工作。在此阶段,组件仍然拥有所有的功能,任何东西都还未被销毁。
8. unMounted 和 onUnmounted
在组件销毁之后触发。
export default {
setup() {
onUnmounted(() => {
console.log('unmounted');
});
},
unmounted() {
console.log('unmounted');
}
}
此时,大多数组件和它的属性已经销毁,所以你能做的不多。