Vue的生命周期
1.什么事生命周期
所谓的生命周期是指:一个事物从出生到最终的死亡,整个经历的过程叫做生命周期。
例如人的生命周期:
(1) 出生:打疫苗
(2) 3岁了:上幼儿园
(3) 6岁了:上小学
(4) 12岁了:上初中
(5) …
(6) 58岁了:退休
(7) …
(8) 临终:遗嘱
(9) 死亡:火化
可以看到,在这个生命线上有很多不同的时间节点,在不同的时间节点上去做不同的事儿。
Vue的生命周期指的是:vm对象从创建到最终销毁的整个过程。
(1) 虚拟DOM在内存中就绪时:去调用一个a函数
(2) 虚拟DOM转换成真实DOM渲染到页面时:去调用一个b函数
(3) Vue的data发生改变时:去调用一个c函数
(4) …
(5) Vue实例被销毁时:去调用一个x函数
在生命线上的函数叫做钩子函数,这些函数是不需要程序员手动调用的,由Vue自动调用,程序员只需要按照自己的需求写上,到了那个时间点自动就会执行。
2 掌握Vue的生命周期有什么用
研究Vue的生命周期主要是研究:在不同的时刻Vue做了哪些不同的事儿。
例如:在vm被销毁之前,需要将绑定到元素上的自定义事件全部解绑,
那么这个解绑的代码就需要找一个地方写一下,写到哪里呢?你可以写到beforeDestroy()这个函数中,这个函数会被Vue自动调用,而且是在vm对象销毁前被自动调用。
像这种在不同时刻被自动调用的函数称为钩子函数。每一个钩子函数都有对应的调用时间节点。
换句话说,研究Vue的生命周期主要研究的核心是:在哪个时刻调用了哪个钩子函数。
在实际项目开发中,对生命周期的准确把握有助于优化代码结构、提升性能以及解决各类潜在问题。比如,在一个复杂的表单页面中,可能需要在created钩子函数中初始化表单数据,以确保页面加载时数据的完整性;而在beforeUpdate钩子函数中,对即将更新的数据进行预处理,防止无效数据导致页面渲染异常。
3 Vue生命周期的4个阶段8个钩子
Vue的生命周期可以被划分为4个阶段:初始阶段、挂载阶段、更新阶段、销毁阶段。
每个阶段会调用两个钩子函数。两个钩子函数名的特点:beforeXxx()、xxxed()。
8个生命周期钩子函数分别是:
(1) 初始阶段
① beforeCreate() 创建前
② created() 创建后
(2) 挂载阶段
① beforeMount() 挂载前
② mounted() 挂载后
(3) 更新阶段
① beforeUpdate() 更新前
② updated() 更新后
(4) 销毁阶段
① beforeDestroy() 销毁前
② destroyed() 销毁后
8个钩子函数写在哪里?直接写在Vue构造函数的options对象当中。
Vue官方的生命周期图:
<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() {// 销毁vmthis.$destroy();},},watch: {counter() {console.log("counter被监视一次!");},},/*1.初始阶段el有,template也有,最终编译template模板语句。el有,template没有,最终编译el模板语句。el没有的时候,需要手动调用 vm.$mount(el) 进行手动挂载,然后流程才能继续。此时如果template有,最终编译template模板语句。el没有的时候,需要手动调用 vm.$mount(el) 进行手动挂载,然后流程才能继续。此时如果没有template,最终编译el模板语句。结论:流程要想继续:el必须存在。el和template同时存在,优先选择template。如果没有template,才会选择el。*/beforeCreate() {// 创建前// 创建前指的是:数据代理和数据监测的创建前。// 此时还无法访问data当中的数据。包括methods也是无法访问的。console.log("beforeCreate", this.counter);// 调用methods报错了,不存在。//this.add()},created() {// 创建后// 创建后表示数据代理和数据监测创建完毕,可以访问data中的数据了。console.log("created", this.counter);// 可以访问methods了。//this.add()},// 2.挂载阶段beforeMount() {// 挂载前console.log("beforeMount");// debugger //断点},mounted() {// 挂载后console.log("mounted");// 创建真实dom元素console.log(this.$el);console.log(this.$el instanceof HTMLElement);},// 3.更新阶段beforeUpdate() {// 更新前console.log("beforeUpdate");},updated() {// 更新后console.log("updated");},// 4.销毁阶段beforeDestroy() {// 销毁前,解绑vueconsole.log("beforeDestroy");console.log(this);this.counter = 1000;},destroyed() {// 销毁后,vm依然存在,不过是vm身上的事件,监听解绑console.log("destroyed");console.log(this);},});</script>
</body>
在Vue组件化开发中,每个组件都有自己独立的生命周期。当一个父组件包含多个子组件时,子组件的生命周期钩子函数也会按照各自的顺序执行。例如,在父组件的mounted钩子函数中,可以获取到所有已挂载的子组件实例,通过this.$children
来操作子组件的属性和方法,实现父子组件之间更灵活的交互。
4 初始阶段做了什么事儿
做了这么几件事:
(1) 创建Vue实例vm(此时Vue实例已经完成了创建,这是生命的起点)
(2) 初始化事件对象和生命周期(接产大夫正在给他洗澡)
(3) 调用beforeCreate()钩子函数(此时还无法通过vm去访问data对象的属性)
(4) 初始化数据代理和数据监测
(5) 调用created()钩子函数(此时数据代理和数据监测创建完毕,已经可以通过vm访问data对象的属性)
(6) 编译模板语句生成虚拟DOM(此时虚拟DOM已经生成,但页面上还没有渲染)
该阶段适合做什么?
beforeCreate:可以在此时加一些loading效果,自定义一些属性放到this身上,好比全局事件总线。例如,在一个大型单页应用中,可以在beforeCreate钩子函数中创建一个全局的事件总线对象,方便各个组件之间进行通信。
created:结束loading效果。也可以在此时发送一些网络请求,获取数据。也可以在这里添加定时器。(最早开始操作数据的地方)在created钩子函数中发送网络请求获取数据,能够在页面渲染之前就准备好必要的数据,避免页面加载后出现长时间的空白等待。同时,在这个阶段添加定时器,可以实现一些定时任务,如轮询获取实时数据等。
5 挂载阶段做了什么事儿
做了这么几件事:
(1) 调用beforeMount()钩子函数(此时页面还未渲染,真实DOM还未生成,此时操作数据,不会最终显示在页面中)
(2) 给vm追加 e l 属性,用它来代替” e l ”, el属性,用它来代替”el”, el属性,用它来代替”el”,el代表了真实的DOM元素(此时真实DOM生成,页面渲染完成)
(3) 调用mounted()钩子函数
该阶段适合做什么?
mounted:可以操作页面的DOM元素了。也有人喜欢在这个阶段请求数据(也可以,但不是很好)
面试题:请求数据是在哪个钩子里,原因是什么?
created()和mounted()都可以请求数据,但是最好在created()中,因为created()最早开始操作数据的地方,而在mounted()中页面已经渲染了,此时再次请求数据时,可能会造成闪屏,因为重新获取请求数据时会对页面重新渲染,此时操作DOM元素时容易有闪屏的现象。不过,在一些特殊场景下,如需要根据页面DOM结构来决定请求的数据内容时,在mounted钩子函数中请求数据也是合理的选择。例如,在一个需要根据页面可视区域大小来请求不同分辨率图片数据的场景中,就必须在mounted阶段获取到页面DOM元素的尺寸信息后再进行数据请求。
6 更新阶段做了什么事儿
(1) data发生变化(这是该阶段开始的标志)
(2) 调用beforeUpdate()钩子函数(此时只是内存中的数据发生变化,页面还未更新)
(3) 虚拟DOM重新渲染和修补
(4) 调用updated()钩子函数(此时页面已更新)
该阶段适合做什么?
beforeUpdate:适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器。在beforeUpdate钩子函数中,可以检查当前DOM状态,根据数据变化前的DOM结构,提前移除一些不再需要的事件监听器,避免内存泄漏。
updated:页面更新后,如果想对数据做统一处理,可以在这里完成。例如,在一个数据展示页面中,当数据更新后,可能需要对新数据进行格式化处理,使其在页面上以更友好的方式展示,这时就可以在updated钩子函数中实现。
7 销毁阶段做了什么事儿
做了这么几件事:
(1) vm.$destroy()方法被调用(这是该阶段开始的标志)
(2) 调用beforeDestroy()钩子函数(此时Vue实例还在。虽然vm上的监视器、vm上的子组件、vm上的自定义事件监听器还在,但是它们都已经不能用了。此时修改data也不会重新渲染页面了)
(3) 卸载子组件和监视器、解绑自定义事件监听器
(4) 调用destroyed()钩子函数(虽然destroyed翻译为已销毁,但此时Vue实例还在,空间并没有释放,只不过马上要释放了,这里的已销毁指的是vm对象上所有的东西都已经解绑完成了)
该阶段适合做什么?
beforeDestroy:适合做销毁前的准备工作,和人临终前写遗嘱类似。例如:可以在这里清除定时器,解绑自定义事件,解除监听等。在一个包含大量定时器和自定义事件的复杂组件中,在beforeDestroy钩子函数中进行清理工作,可以确保组件在销毁时不会留下任何无效的资源占用,提高应用的性能和稳定性。