文章目录
生命周期钩子 | Vue 2 | Vue 3 (Composition API) | 备注 |
---|---|---|---|
创建前 | beforeCreate | 无直接对应 | 在数据观测和初始化事件/侦听器之前调用 |
创建后 | created | setup | 实例已被创建,完成数据观测,但尚未挂载 |
挂载前 | beforeMount | onBeforeMount | `在挂载开始之前被调用 |
挂载后 | mounted | onMounted | 实例被挂载后调用 |
更新前 | beforeUpdate | onBeforeUpdate | 数据更新时调用,发生在虚拟 DOM 打补丁之前 |
更新后 | updated | onUpdated | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子 |
卸载前 | beforeDestroy | onBeforeUnmount | 实例销毁之前调用。在这一步,实例仍然完全可用 |
卸载后 | destroyed | onUnmounted | Vue 实例销毁后调用。调用后,所有的事件监听器都会被移除,所有的子实例也都会被销毁 |
激活时 | activated | onActivated | keep-alive 组件激活时调用 |
失活时 | deactivated | onDeactivated | keep-alive 组件停用时调用 |
错误捕获 | errorCaptured | onErrorCaptured | 当捕获一个来自子孙组件的错误时被调用 |
Vue2 生命周期
- beforeCreate:创建之前(
el
、data
和message
都还是undefined
,不可用的) - created:创建完毕(能读取到数据
data
的值,但是DOM
还没生成) - beforeMount:挂载之前(生成
DOM
,但此时{{ message }}
还没有挂载data
中的数据) - mounted:挂载完毕(
{{ message }}
已经成功挂载渲染data
的值) - beforeUpdate:更新之前
- updated:更新完毕
- beforeDestroy:销毁之前
- destroyed:销毁完毕(实例与视图的关系解绑,再修改
message
的值,视图再也不会更新了) - activated:
keep-alive
组件激活时调用 - deactivated:
keep-alive
组件停用时调用
注:
activated
和deactivated
是比较特殊的两个钩子,需要keep-live
配合使用- 当引入
keep-alive
的时候,页面第一次进入,钩子的触发顺序created
=>mounted
=>activated
,退出时触发deactivated
。当再次进入(前进或者后退)时,只触发activated
。
举例代码:
<body>
<div id="app">
{{ message }}
</div>
<script>
let app = new Vue({
el: "#app",
data: {
message: 'xia',
},
beforeCreate() {
console.group("1. beforeCreate() =====> 创建之前");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
console.log("%c%s", "color:red", "el、data和message都还是undefined,不可用的");
console.log("-------------------------------------------");
},
created() {
console.group("2. created() =====> 创建完毕");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
console.log("%c%s", "color:red", "能读取到数据data的值,但是DOM还没生成");
console.log("-------------------------------------------");
},
beforeMount() {
console.group("3. beforeMount() =====> 挂载之前");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
console.log("%c%s", "color:red", "生成DOM,但此时{{ message }}还没有挂载data中的数据");
console.log("-------------------------------------------");
},
mounted() {
console.group("4. mounted() =====> 挂载完毕");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
console.log("%c%s", "color:red", "{{ message }}已经成功挂载渲染data.name的值:Xia");
console.log("-------------------------------------------");
},
beforeUpdate() {
console.group("5. beforeUpdate() =====> 更新之前");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
console.log("-------------------------------------------");
},
updated() {
console.group("6. beforeUpdate() =====> 更新完毕");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
console.log("-------------------------------------------");
},
beforeDestroy() {
console.group("7. beforeDestroy() =====> 销毁之前");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
console.log("-------------------------------------------");
},
destroyed() {
console.group("8. beforeDestroy() =====> 销毁完毕");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
console.log("%c%s", "color:red", "实例与视图的关系解绑,再修改message的值,视图再也不会更新了");
console.log("-------------------------------------------");
}
});
</script>
</body>
create 和 mounted
beforecreated
:Vue 实例的挂载元素$el 和 数据对象data 并未初始化
created
:完成了 data 数据的初始化,$el没有
beforeMount
:完成了 $el 和 data 初始化,但还是挂载在虚拟的 DOM 节点上
mounted
:Vue 实例挂载到实际的 DOM 上
.
另外在标红处,我们能发现 $el还是 {{message}},这里就是应用的 Virtual DOM(虚拟Dom)技术,先把坑占住了,到后面mounted挂载的时候再把值渲染进去
。
update
这里我们在 chrome console里执行以下命令
app.message = 'bing'
下面就能看到data里的值被修改后,将会触发update的操作。
destroy
这里我们在console里执行下命令对 vue实例进行销毁
app.$destroy();
销毁完成后,我们再重新改变message的值,vue不再对此动作进行响应了。但是原先生成的dom元素还存在,执行了destroy操作,后续就不再受vue控制了。
activated
比较特殊的一个钩子,需要
keep-live
配合使用
activated
:动态组件初始化渲染过程中调用 keep-alive组件激活时调用。
deactivated
:动态组件移出过程中调用 keep-alive组件停用时调用。
当引入 keep-alive
的时候,页面第一次进入,钩子的触发顺序 created
=> mounted
=> activated
,退出时触发 deactivated
。当再次进入(前进或者后退)时,只触发activated
。
keep-alive 之后页面模板第一次初始化解析变成HTML片段后,再次进入就不在重新解析而是读取内存中的数据,即,只有当数据变化时,才使用VirtualDOM进行diff更新。故,页面进入的数据获取应该在activated中也放一份。数据下载完毕手动操作DOM的部分也应该在activated中执行才会生效。
所以,应该activated中留一份数据获取的代码,或者不要created部分,直接将created中的代码转移到activated中。
⚡️Vue2 生命周期总结
1. beforecreate
执行时组件实例还未创建,并未初始化
-
data:未被创建,值为 undefined
-
el:未被创建,值为 undefined
-
this:指向创建的实例,但不能访问到 data、computed、watch、methods 上的方法和数据
一般用于设置title、keywords等。
.
2. created
组件初始化完毕,各种数据可以使用,处于未挂载阶段
-
data:完成了初始化,可以访问和修改其中的属性。
-
el:仍然不可用,组件尚未被挂载到 DOM 中
-
this:指向组件实例,可以访问组件实例上的所有属性和方法:this.data 可以访问,watcher、event 和 methods 也已经可用。
组件首次访问data、methods等;一般用于数据初始化、ajax请求。
.
3. beforeMount
-
data:完成了初始化,可以访问和修改其中的属性。
-
el:完成了初始化,但尚未挂载到真实 DOM,还没有被渲染成最终的 DOM 结构,因此不能访问到真实的 DOM 元素,el 属性仅是虚拟的元素节点。
-
this:指向的是 Vue 组件的实例。可以通过 this 访问组件实例上的所有属性和方法,包括响应式数据、计算属性、方法、事件等。由于组件实例已经完成了数据观测等初始化工作,因此 this 上的数据和方法都是可用的。就不是能用来获取DOM~
用来做一些初始化的工作,例如从服务器加载数据、进行一些计算或者准备一些其他的操作。
.
4. mounted
-
data:完成了初始化,可以访问和修改其中的属性。
-
el:完成了 DOM 元素的挂载,已经可以进行 DOM 操作。
-
this:指向的是 Vue 组件的实例。可以通过 this 访问组件实例上的所有属性和方法。包括对DOM的操作
模板已经真实地挂载到了页面中,用户已经可以看到渲染好的页面了。
这时可以操作 DOM、发送请求、初始化或使用其他第三方库等
.
5. beforeUpdate
更新前,在组件的响应式数据发生变化,并且由此导致的虚拟 DOM 重新渲染和打补丁(patching),即将应用于真实 DOM 之前被调用
当组件的状态即将更新,但尚未反映到 DOM 上时,这个钩子会被触发。因此还不能访问到更新后的 DOM。
-
data:数据已经是最新的,但还没有触发视图的重新渲染。因此,此时你可以访问到更新后的数据值。
-
el:仍然指向旧的 DOM 元素,它还没有被更新以反映 data 中的新变化。
这意味着,尽管 data 中的数据已经更新,但用户看到的页面内容还是旧的,因为新的 DOM 元素还没有被创建和挂载。 -
this:指向的是 Vue 组件的实例。可以通过 this 访问到组件实例上的所有属性和方法,包括已经更新的 data。
尽管 this.data 反映了最新的数据状态,但实际的 DOM 元素(this.$el)还没有更新。
.
6. updated
更新后,在组件的虚拟 DOM 已经重新渲染和打补丁(patching)到真实的 DOM 之后被调用
当组件的响应式数据发生变化,并且这些变化已经反映到组件的视图上时,updated 钩子就会被触发。
这时可以访问到组件更新后的 DOM,也可以对更新后的状态进行操作,但应避免在该钩子中对状态进行修改,因为这可能会导致无限循环的更新。
-
data:数据已经是最新的,且视图已经重新渲染。
-
el:指向的 DOM 元素已经更新
此时,用户看到的页面内容是与 data 中的数据保持一致的,可以通过 this.$el 访问到这个更新后的 DOM 元素,并进行后续的 DOM 操作或查询。 -
this:指向的是 Vue 组件的实例。可以访问到最新的数据和方法,并且 DOM 已经与最新的数据保持同步,因此你可以在 this 的上下文中执行依赖于最新 DOM 状态的操作。
.
7. beforeDestroy
组件实例即将被销毁,用于在组件实例销毁之前执行一些清理工作
这时可以执行如移除事件监听器、取消定时器、销毁子组件或执行其他必要的清理工作,防止内存泄漏
在 Vue 3 中,beforeDestroy 被重命名为 beforeUnmount
-
data:数据仍然是可以访问和使用的
-
el:指向的 DOM 元素仍然存在,并且与组件的当前状态相对应
此时,你可以通过 this.$el 访问到这个 DOM 元素,并执行一些最后的 DOM 操作或查询 -
this:指向的是 Vue 组件的实例。仍可以访问组件实例上的所有属性和方法。
.
8. destroyed
组件实例已经被完全销毁,在组件实例已经被销毁和清理之后调用
此时Vue 实例上的所有东西都被销毁,所有的事件监听器会被移除。因此无法访问组件的任何状态或方法。该钩子在服务器端渲染期间不被调用。
主要用于执行一些不依赖于组件实例本身的清理工作,比如记录日志、发送统计信息等。
-
data:数据仍然可以被访问(如果你还持有组件实例的引用),但此时它们已经与 Vue 的响应式系统断开连接,不再具有响应性
-
el:指向的 DOM 元素已经被移除,不再与组件实例关联。
-
this:仍然指向组件实例,但由于组件已经被销毁,这个实例上的大多数方法和属性都已经不再有效。
尤其是与响应式数据和 DOM 相关的属性和方法,如 this.data 和 this.$el,都将不再可靠。
示意图:
⚡️Vue2 父子组件生命周期执行顺序
1. 首次加载渲染过程
- 父beforeCreate
- 父created
- 父beforeMount
- 子beforeCreate
- 子created
- 子beforeMount
- 子mounted
- 子activated
- 父mounted
2. 父组件更新过程
- 父beforeUpdate
- 子deactivated
- 父updated
3. 子组件更新过程
- 父beforeUpdate
- 子beforeUpdate
- 子updated
- 父updated
4. 销毁过程
- 父beforeDestroy
- 子beforeDestroy
- 子destroyed
- 父destroyed
activated和deactivated只会在父组件中使用keep-alive
时才会被调用。
keep-alive 中的生命周期
keep-alive是 Vue 提供的一个内置组件,用来对组件进行缓存——在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
这意味着当组件再次被激活时,它将不会被重新渲染或重新初始化,而是保持其之前的状态和数据。这对于提高页面性能和用户体验特别有用,特别是当你需要在不同组件之间频繁切换时。
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
在上面的例子中,currentComponent 是一个变量,用于动态地确定当前要显示的组件。通过使用 keep-alive,你可以确保即使切换组件,它们的状态也会被保留。
-
条件渲染
虽然 keep-alive 可以缓存组件实例,但它仍然需要组件被渲染。
如果组件的条件渲染表达式(如 v-if)为 false,则组件不会被渲染,因此也不会被 keep-alive 缓存。 -
生命周期钩子
当组件被 keep-alive 缓存时,其生命周期钩子函数的行为会发生变化:-
会多出两个生命周期:deactivated、activated
-
当组件被切换出去时:beforeDestroy 和 destroyed 钩子不会被触发,因为组件不会被真正销毁。
组件会被缓存到内存中,同时 deactivated 钩子会被调用 -
当组件再次被激活时:activated 钩子会被调用,而不是 created 或 mounted。会从缓存里找组件
-
-
全生命周期
- 首次进入:
beforeCreate
=>created
=>beforeMount
=>mounted
=>activated
- 离开组件:
deactivated
(不会触发beforeDestroy
和destroyed
钩子) - 再次进入:
activated
(不会触发beforeCreate
、created
、beforeMount
和mounted
钩子) - 组件被销毁:
beforeDestroy
=>destroyed
- 首次进入:
数据请求在created和mouted的区别
created
是在组件实例一旦创建完成的时候立刻调用,这时候页面dom节点并未生成;
mounted
是在页面dom节点渲染完毕之后就立刻执行的。触发时机上created是比mounted要更早的
两者的相同点:都能拿到实例对象的属性和方法。
讨论这个问题本质就是触发的时机,放在mounted中的请求有可能导致页面闪动(因为此时页面dom结构已经生成),但如果在页面加载前完成请求,则不会出现此情况。建议对页面内容的改动放在created生命周期当中。
另外,SSR不支持 beforeMount 、mounted 钩子函数,放在 created 中有助于一致性。