介绍
生命周期函数:
每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。
setup => beforeCreate =>created =>beforeMount =>mounted -->beforeUpdate=>updated-->
beforeUnmount=>unmounted
演示讲解
<template>
<div class="hello">
<div style="height:50px" ref="spanRef">{{ msg }}</div>
<button @click="clickBtn">button</button>
</div>
</template>
<script >
export default {
name: 'appName',
data() {
return {
msg: 'msg',
}
},
methods: {
clickBtn() {
this.msg = 'newMsg'
}
},
setup() {
console.log('----setup----')
console.log(this)
},
beforeCreate() {
console.log('----beforeCreate----')
console.log(this)
console.log(this.msg) //msg
console.log(this.$el)
},
created() {
console.log('----created----')
console.log(this)
console.log(this.msg) //msg
console.log(this.$el)
},
beforeMount() {
console.log('----beforeMount----')
console.log(this)
console.log(this.msg)
console.log(this.$el)
},
mounted() {
console.log('----mounted----')
console.log(this.msg) //msg
console.log(this.$el)
},
beforeUpdate() {
console.log('----beforeUpdate----')
console.log(this.$el)
console.log(this.$refs.$el)
console.log(this.msg) //msg
for (var i = 0; i <= 10; i++) {
console.log(1)
}
},
updated() {
console.log('----updated----')
console.log(this.$refs)
console.log(this.msg) //msg
},
beforeUnmount() {
console.log('----beforeDestroy----')
console.log(this.msg) //msg
console.log(this.$el)
},
unmounted() {
console.log('----destroyed----')
console.log(this.msg) //msg
console.log(this.$el)
}
}
</script>
下面我来解释一下代码,就是使用了上述生命周期函数,在函数中打印了vue实例对象(this),数据(this.msg),组件的真实DOM(this.$el)。
1.setup
setup组件初始化之前执行,在setup函数中this 还不是组件实例,this此时是undefined
2.beforeCreate
执行beforeCreate生命周期函数 前初始化了事件以及定义createElement函数,定义了一些属性。
这时我们就可以打印出vue实例对象了,但是调用不了数据。
beforeCreate执行完后,会开始进行数据初始化,这个过程,会定义data数据,方法以及事件,并且完成数据劫持observe以及给组件实例配置watcher观察者实例。这样,后续当数据发生变化时,才能感知到数据的变化并完成页面的渲染
3.created
created生命周期函数,当这个函数执行的时候,我们已经可以拿到data下的数据以及methods下的方法了,所以在这里,我们可以开始调用方法进行数据请求了
4.beforMount
调用beforMount, 也就是说实际从creted到beforeMount之间,最主要的工作就是将模板或者el转换为render函数。并且我们可以看出一点,就是你不管是用el,还是用template, 或者是用我们最常用的.vue文件(如果是.vue文件,他其实是会先编译成为template),最终他都是会被转换为render函数的。
beforeMount调用后,我们是不是要开始渲染render函数了,首先我们会先生产一个虚拟dom(用于后续数据发生变化时,新老虚拟dom对比计算),进行保存,然后再开始将render渲染成为真实的dom。
5.mounted
调用mounted,并将标识生命周期的一个属性_isMounted 置为true。所以mounted函数内,我们是可以操作dom的,因为这个时候dom已经渲染完成了
6.beforeupdated
当我们状态数据发生变化时,我们在触发beforeUpdate,要开始将我们变化后的数据渲染到页面上了
点击按钮触发更新
beforeUpdate调用之后,我们又会重新生成一个新的虚拟dom(Vnode),然后会拿这个最新的Vnode和原来的Vnode去做一个diff算,这里就涉及到一系列的计算,算出最小的更新范围,从而更新render函数中的最新数据,再将更新后的render函数渲染成真实dom。也就完成了我们的数据更新
7.updated
updated里面也可以操作dom,并拿到最新更新后的dom。不过这里我要插一句话了,mouted和updated的执行,并不会等待所有子组件都被挂载完成后再执行,所以如果你希望所有视图都更新完毕后再做些什么事情,那么你最好在mouted或者updated中加一个$nextTick(),然后把要做的事情放在$netTick()中去做
8.beforeUnmount unmounted
beforeUnmount 钩子函数是在组件实例销毁之前调用的,而 unmounted 钩子函数是在组件实例销毁之后调用的。这两个钩子函数提供了在组件生命周期的特定时刻执行逻辑的机会。
以下是一些常见的使用场景:
-
清除定时器或取消异步请求:在组件销毁之前,你可能需要清除定时器或取消尚未完成的异步请求,以避免内存泄漏或意外的副作用。
-
取消订阅事件或取消监听器:如果你在组件中订阅了全局事件或添加了事件监听器,那么在组件销毁之前,应该取消订阅或移除监听器,以避免不必要的事件处理或内存泄漏。
-
清理资源:如果你在组件中使用了一些外部资源,比如打开了一个 WebSocket 连接、订阅了一个消息队列等,那么在组件销毁之前,应该关闭连接或取消订阅,以释放资源。
-
取消订阅 Vuex store:如果你在组件中使用了 Vuex 状态管理库,并且订阅了 store 的某些状态,那么在组件销毁之前,应该取消对 store 的订阅,以避免不必要的状态更新。
这些逻辑应该在 beforeUnmount 或 unmounted 钩子函数中执行,因为这些钩子函数提供了在组件销毁之前或之后执行逻辑的时机。这样可以确保在组件销毁之前完成必要的清理工作,避免潜在的问题和资源泄漏。
Composition API 形式的生命周期钩子
介绍
Composition API也称为组合式API,实现代码的共享以及重用
Vue3.2 中 只需要在 script 标签上加上 setup 属性,组件在编译的过程中代码运行的上下文是在 setup() 函数中,无需return,template可直接使用
你可以通过在生命周期添加“on”的前缀来访问一个组件的生命周期钩子 。下面这个表格描述了如何在setup()内调用生命周期钩子:
选项 API | setup内部的钩子 |
---|---|
beforeCreate | 不需要 |
created | 不需要 |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
演示
<template>
<div>
<h1>我是Holle组件</h1>
<h3>当前的值是:{{ sum }}</h3>
<!-- 修改数据触发更新阶段的生命周期钩子 -->
<button @click="sum++">点击加1</button>
</div>
</template>
<script setup>
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'
let sum = ref(0);
//通过组合式API的形式去使用生命周期钩子
onBeforeMount(() => {
console.log("---onBeforeMount---");
});
onMounted(() => {
console.log("---onMounted---");
});
onBeforeUpdate(() => {
console.log("---onBeforeUpdate---");
});
onUpdated(() => {
console.log("---onUpdated---");
});
onBeforeUnmount(() => {
console.log("---onBeforeUnmount---");
});
onUnmounted(() => {
console.log("---onUnmounted---");
});
</script>
点击加一
我们在App组件中使用了一个Hello组件 为了能具体的演示生命周期的卸载阶段 我们在Hello组件加了一个v-if判断来演示组件的卸载
<template>
<div>
<button @click="isShow = !isShow ">点击是否显示组件</button>
<!-- if对应的表达式为假 组件将被卸载 -->
<Hello v-if="isShow"/>
</div>
</template>
<script>
import { ref } from "vue";
import Hello from './components/Hello.vue'
export default {
name: "App",
components: {
Hello
},
setup() {
// 为了能演示出来卸载的效果我们在这定义一个布尔值 是否显示组件
let isShow = ref(true)
return {
isShow
}
},
};
</script>
点击按钮
注意
1.为什么要设计生命周期函数?
答案:为了更好的设计程序(区分业务),让代码更有逻辑和可维护性
- 把整个运行期间的业务区分的很明显
- 能够更好的帮助我们把产品的业务逻辑实现了
- 更有利于我们维护产品 和 修改需求
- 能够让我们写出更高质量的产品的代码
2.页面首次加载过程中,会依次触发哪些钩子?
答案:beforeCreate
、created
、beforeMount
、mounted
3.this.$el是什么?它在哪些钩子中才能访问?、
答案:它代表了当前组件的真实DOM,要在mounted之后才有
4.Vue实例的data属性,在哪些钩子中能访问?
答案:created
、beforeMount
、mounted
、beforeUpdate
、updated
、beforeDestroy
5.为什么不要在更新钩子中做页面的数据请求?
答案:会导致死循环,react有一个shoudCoponentUpdate可以自己控制,但是vue中没有
6.你用beforeCreate做过什么业务?
答案:这个钩子中可以做网络请求,但是vm没有构建完毕,此时数据方法等等的劫持还没有完成,不能操作this,因此可以做预加载