目录
一、Vue实例的生命周期
生命周期即是一个事物从出生到消亡的过程,世间万物都有生命周期,Vue实例也是如此。Vue的生命周期是Vue中比较重要的知识点,在面试中常常被问到。
简单来说可以分为四个阶段:创建阶段,挂载阶段,运行阶段,销毁阶段。在这个过程中会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
每个阶段都有两个生命周期钩子函数:
- 创建阶段–beforeCreate,created
- 挂载阶段–beforeMount,mounted
- 运行阶段–beforeUpdate,updated
- 销毁阶段–beforeDestroy,destroyed
通过官方的图可以直观的看到这几个阶段 Vue 帮我们做了什么,以及我们又能在不同阶段做些什么:
创建阶段
此时Vue刚刚出生,组件开始初始化并且开始观察数据,此阶段有beforeCreate 和 created 两个生命周期钩子函数。
beforeCreate
是new Vue()之后触发的第一个钩子,此时 data、methods、computed以及watch上的数据和方法还未初始化,都不能被访问。
created
在实例创建完成后被立即调用,此时已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。然而,挂载阶段还没开始,$el property 目前尚不可用,所以无法与Dom进行交互。
挂载阶段
这个阶段是vue实例的出生阶段,这个阶段将实现 DOM 的挂载,这标志着我们可以在浏览器里中看到页面了。
beforeMount
发生在挂载之前,在这之前 template 模板已导入渲染函数编译。此时虚拟Dom已经创建完成,即将开始渲染,此时页面仍是空白。在这一阶段也可以对数据进行更改,不会触发updated。
mounted
在挂载完成后发生,此时真实的Dom挂载完毕,vue实例已经初始化完成,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。
运行阶段
当vue实例中的数据发生改变时,DOM 也会发生变化,此阶段中Vue将持续监听数据,随时渲染并更新DOM。
beforeUpdate
发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重新渲染,但会再次触发当前钩子函数。
updated
发生在更新完成之后,此时 Dom 已经更新。现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,最好使用计算属性或 watcher 取而代之。最好不要在此期间更改数据,因为这可能会导致无限循环的更新。
销毁阶段
一旦$destory()被调用,Vue实例进入消亡阶段,实例还可以被使用,直到destroyed(),我们可以为Vue实例处理一些“后事”。
beforeDestroy
发生在实例销毁之前,在这期间实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。
destroyed
发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,事件监听器被移除,所有子实例也统统被销毁。
以上就是Vue的整个生命周期,可以写个简单的例子验证一下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<button @click="update">更新</button>
<button @click="destroy">销毁</button>
<div>{{msg}}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
msg:"marmot"
},
methods: {
update() {
this.msg = '土拨鼠'
},
destroy() {
this.$destroy()
}
},
beforeCreate() {
console.log('beforeCreate执行');
},
created() {
console.log('created执行');
},
beforeMount() {
console.log('beforeMount执行');
},
mounted() {
console.log('mounted执行');
},
beforeUpdate() {
console.log('beforeUpdate执行');
},
updated() {
console.log('updated执行');
},
beforeDestroy() {
console.log('beforeDestroy执行');
},
destroyed() {
console.log('destroyed执行');
}
})
</script>
</body>
</html>
执行结果:
二、父子组件的生命周期
Vue单个组件的生命周期我们已经了解了,但是,如果有嵌套组件,父子组件的生命周期的执行顺序又有稍许不同。
创建与挂载阶段
该过程主要涉及 beforeCreate、created、beforeMount、mounted 4 个钩子函数。
当父组件执行完beforeMount挂载开始后,会依次执行子组件中的钩子,直到全部子组件mounted挂载到实例上,父组件才会进入mounted钩子
执行顺序为:
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
一定得等所有子组件挂载完毕后,父组件才能挂载完毕,所以父组件的 mounted 在最后。
更新阶段
该过程主要涉及 beforeUpdate、updated 2 个钩子函数。注意,当父子组件有数据传递时,才有这个更新阶段执行顺序的比较。
执行顺序为:
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
销毁阶段
该过程主要涉及beforeDestroy、destroyed 2 个钩子函数。
执行顺序为:
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
这里用项目的单文件组件的形式举个例子:
<!-- App.vue -->
<template>
<div id="app">
<button @click="update">更新</button>
<button @click="destroy">销毁</button>
<my-cpn :msg="msg"></my-cpn>
</div>
</template>
<script>
import MyCpn from "./components/MyCpn.vue";
export default {
name: "App",
components: {
MyCpn,
},
data() {
return {
msg:'marmot'
}
},
methods: {
update() {
this.msg = "土拨鼠";
},
destroy() {
this.$destroy();
},
},
beforeCreate() {
console.log("父组件beforeCreate执行");
},
created() {
console.log("父组件created执行");
},
beforeMount() {
console.log("父组件beforeMount执行");
},
mounted() {
console.log("父组件mounted执行");
},
beforeUpdate() {
console.log("父组件beforeUpdate执行");
},
updated() {
console.log("父组件updated执行");
},
beforeDestroy() {
console.log("父组件beforeDestroy执行");
},
destroyed() {
console.log("父组件destroyed执行");
},
};
</script>
<style>
</style>
<!-- MyCpn.vue -->
<template>
<p>{{ msg }}</p>
</template>
<script>
export default {
props: {
msg: String,
},
beforeCreate() {
console.log("子组件beforeCreate执行");
},
created() {
console.log("子组件created执行");
},
beforeMount() {
console.log("子组件beforeMount执行");
},
mounted() {
console.log("子组件mounted执行");
},
beforeUpdate() {
console.log("子组件beforeUpdate执行");
},
updated() {
console.log("子组件updated执行");
},
beforeDestroy() {
console.log("子组件beforeDestroy执行");
},
destroyed() {
console.log("子组件destroyed执行");
},
};
</script>
<style>
</style>
执行结果: