从0到Vue3企业项目实战【05.vue生命周期】
什么是生命周期?
引言
人有生老病死,生老病死就是人的生命周期,vue的生命周期也是如此,从创建到销毁的整个过程
就是Vue的生命周期
,我们要了解的是vue组件
各生命周期会触发内置的钩子函数
,我们可以在特定的钩子函数中执行特定的操作,如初始化数据等等
生命周期的分类
vue的生命周期分为四大阶段
,8个方法
-
阶段分为
初始化
,挂载
,更新
,销毁
-
钩子函数分别为
beforeCreate
,created
,beforeMount
,mounted
,beforeUpdate
,updated
,beforeDestroy
,destroyed
两两对应与阶段
阶段 | 方法 | 方法 |
---|---|---|
初始化 | beforeCreate | created |
挂载 | beforeMount | mounted |
更新 | beforeUpdate | updated |
销毁 | beforeDestroy | destroyed |
带before即代表阶段前,如初始化前等等,其实我们只要记住四个就可以吧所有全部记住了
生命周期详解
这是来自于官网的生命周期图示,你不需要一开始就完全理解,当随着你学习的深入,这个图的参考价值会越来越高
,当然现阶段还是介绍vue2的生命周期
,其实领悟了vue2的生命周期vue3没有太大的改变
来我们一步步分析整个生命周期的过程
- newVue() -> vue实例化(组件其实也是一个vue实例)
- init Evemts & Lifecycle -> 初始化事件与生命周期函数
- beforeCreate -> 初始化前生命周期钩子函数被执行(此钩子函数响应时,data和method还未挂载在实例对象上,所有还不能使用)
- init injections&reactivity -> vue实例挂载data和methods等
- create -> 初始化生命周期钩子函数被执行,实例创建
- 接下来编译模板开始分析
- Has el options? 是否有el选项? -> 检查要挂载到哪里
- 没有,调用$mount()方法
- 有,继续检查template选项
即检查main.js中,是否有el选项 ,如果有就不需要$mount了两者效果相似,这一项确认了我们要觉得将
vue编译好的模板
挂载在id为app的组件上
new Vue({
el: '#app', // 挂载在index.html的id叫app的标签上
render: h => h(App),
})
new Vue({
render: h => h(App),
}).$mount('#app')
- template选项检查(脚手架创建的vue项目基本上都是有的)
- 有 -> 编译template返回render渲染函数
- 无 -> 编译el选项对应标签作为tempate(要渲染的模板)
- beforeMount -> 虚拟Dom挂载成为真实Dom之前会执行的钩子函数
- Create vm$el and Replace "el" with it -> 吧虚拟Dom和渲染数据一并挂载到真实Dom上
- 真实Dom挂载完毕
- mounted -> 挂载生命周期钩子函数执行
- beforeUpdate -> 当data里的数据发生改变,更新Dom之前会执行的钩子函数
- Virtual Dom re-render and patch -> 虚拟Dom重新渲染.大补丁到真实得Dom
- updated -> 生命周期钩子函数被执行
- 每当有data中的数据发生改变,都会重复这个过程
- beforeDestroy -> 当$destroy()被调用 -> 组件Dom被移除(例如v-if)时调用的生命周期钩子函数
- Teardown watchers child components and event listeners -> 关闭数据监听器,子组件和事件侦听器
- destroyed -> 实例销毁最后执行的一个钩子函数
如何使用生命周期钩子函数?
准备工作
创建一个组件,我们通过该组件来逐步学习使用钩子函数,
lifeCycle.vue
,将该组件在App.vue
中引入并使用
App.vue
<template>
<div>
<LifeCycle v-if="isShow"></LifeCycle>
<button @click="isShow = !isShow">显隐组件</button>
</div>
</template>
<script>
import LifeCycle from "./components/lifeCycle.vue";
export default {
components: { LifeCycle },
data () {
return {
isShow: true
}
}
}
</script>
<style>
</style>
lifeCycle.vue
<template>
<div>
<h1>look控制台的打印</h1>
<p id="myp">{{msg}}</p>
<ul id="myUl">
<li v-for="(item,index) in arr" :key="index">{{item}}</li>
</ul>
<button @click="arr.push(666)">打上666</button>
</div>
</template>
<script>
export default {
data () {
return {
msg: "nice",
arr: ['点赞', '转发', '三连'],
timer: {}
}
},
// 一 初始化
// new Vue()以后 vue内部给实例对象添加一些属性与方法,data和methods初始化之前
beforeCreate () {
console.log("beforeCreate -- 执行", this.msg);
},
// 该生命周期触发代表着 实例已初始化完成,即一下内容可以在在created中使用, data.method.数据侦听,计算属性,方法,事件/侦听器的回调函数
created () {
console.log("created -- 执行", this.msg);
this.timer = setInterval(() => {
console.log('点赞收藏三连');
}, 500)
},
// 二 挂载阶段
// 真实Dom挂载之前
beforeMount () {
console.log("beforeMount -- 执行", document.getElementById("myp"));
this.msg = '挂载阶段改了'
},
// 真实Dom挂载以后
// 场景 : 需要拿到挂载后的真实Dom
mounted () {
console.log("mounted -- 执行", document.getElementById("myp"));
},
// 三 更新阶段
// 当数据发生改变还未开始更新Dom时触发
beforeUpdate () {
console.log("beforeUpdate -- 执行", document.querySelectorAll("#myUl>li")[3]);
},
// 当数据发生改变并更新了Dom后触发
updated () {
console.log("updated -- 执行", document.querySelectorAll("#myUl>li")[3]);
},
// 四 销毁阶段
// 组件Dom被移除之前
// 移除全局事件,移除计时器等,防止内存泄露,eventBus移除事件 $off方法
beforeDestroy () {
console.log("beforeDestroy -- 执行");
clearInterval(this.timer)
},
destroyed () {
console.log("destroyed -- 执行");
}
}
</script>
<style>
</style>
初始化阶段
beforeCreate
触发时,仅创建了实例以及对生命周期函数与事件的初始化
,此时是不能获取到data的值
的,相同的计算属性,侦听器等也未初始化完成,该生命周期函数是都不能获取以及调用的created
组件已完成了初始化
, 可以在该生命周期获取data的值
,使用method,计算属性,侦听器等等
,我们可以在此处进行一些简单的ajax
请求对数据进行初始化等等,此时真实Dom还未进行挂载,所有有对Dom操作都不可进行
beforeCreate () {
console.log("beforeCreate -- 执行", this.msg);
},
created () {
console.log("created -- 执行", this.msg);
this.timer = setInterval(() => {
console.log('点赞收藏三连');
}, 500)
},
我们在该生命周期获取了data中的数据我们可以通过控制台看到
挂载阶段
beforeMount
是在真实Dom挂载前的一个生命周期钩子函数,此时还无法获取到Dommounted
此时真实Dom已经挂载完成,可以在该生命周期中获取Dom,通常
,我会更喜欢在该生命周期中执行数据初始化的ajax请求
,第一呢created
比起来mounted
执行快不了几微秒,对性能的提升是微乎其微的,并且浏览器机制中渲染线程和js线程是互斥的
,所以有可能ajax请求会打断渲染线程,所以请求放在mounted
中更合适
// 二 挂载阶段
// 真实Dom挂载之前
beforeMount () {
console.log("beforeMount -- 执行", document.getElementById("myp"));
this.msg = '挂载阶段改了'
},
// 真实Dom挂载以后
// 场景 : 需要拿到挂载后的真实Dom
mounted () {
console.log("mounted -- 执行", document.getElementById("myp"));
},
我们在
beforeMount
中尝试打印了一下Dom,然后返回的是null,由此可知,该生命周期确实为渲染真实Dom
并且在该生命周期中修改了msg中的值,mounted
中可以看到可以打印出来Dom了,并且msg的值也发生了修改
更新阶段
- 当
组件Dom挂载后
此时组件就会进入一个循环直到组件销毁,循环即每当data中的数据发生改变时,就会执行一次更新阶段的生命周期钩子函数
beforeUpdate
当数据发生更新,还未更新Dom时触发updated
,当更新Dom后就会触发该生命周期钩子函数
// 三 更新阶段
// 当数据发生改变还未开始更新Dom时触发
beforeUpdate () {
console.log("beforeUpdate -- 执行", document.querySelectorAll("#myUl>li")[3]);
},
// 当数据发生改变并更新了Dom后触发
updated () {
console.log("updated -- 执行", document.querySelectorAll("#myUl>li")[3]);
},
注意两者都是可以拿到最新的数据的,不同的是Dom是否更新而已
销毁阶段
created () {
console.log("created -- 执行", this.msg);
this.timer = setInterval(() => {
console.log('点赞收藏三连');
}, 500)
},
beforeDestroy () {
console.log("beforeDestroy -- 执行");
clearInterval(this.timer)
},
destroyed () {
console.log("destroyed -- 执行");
}
我们在created中挂载了一个定时器,我们先看看假如不在beforeDestroy
中清除定时器后会发生什么
可以看到组件销毁后我们设计的定时器依旧在不停的执行,这就是一个内存泄露,当我们在
beforeDestroy
中清除定时器后就解决了
当然,不管是
beforeDestroy
或者destroyed
都可以做清除定时器的操作,两者区别也不是特别大,看业务场景选择使用吧
来看看我的其他章节吧,正在长更中
转载自:https://juejin.cn/post/7125275725359218719