说到生命周期函数,就要来先谈谈生命周期,一个Vue实例从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、销毁等一系列过程,我们称之为Vue的生命周期
;
更通俗的说实例从被创建出来、运行、最后被销毁所要经历的一系列过程,就是生命周期。
在Vue生命周期的不同阶段,内部会自动执行一些称为生命周期钩子的函数,也称之为生命周期函数,也叫生命周期钩子,生命周期事件。
这些生命周期钩子可以在Vue的选项中配置,如下:
<div id="app">
<input type="text" v-model="message">
<button v-on:click="handleDestroy">销毁</button>
<p>{{message}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
message: 'Hello world',
},
methods: {
handleDestroy() {
this.$destroy();
}
},
beforeCreate() {
console.groupCollapsed("%c%s", "color:red", '1、实例创建之前: beforeCreate');
console.log("$el:", this.$el,);
console.log("$data:", this.$data);
console.groupEnd();
},
created() {
console.groupCollapsed("%c%s", "color:red", '2、实例创建完毕: created');
console.log("$el:", this.$el);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
beforeMount() {
console.groupCollapsed("%c%s", "color:red", '3、实例挂载之前: beforeMount');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
mounted() {
console.groupCollapsed("%c%s", "color:red", '4、实例挂载完毕: mounted');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
beforeUpdate() {
console.groupCollapsed("%c%s", "color:red", '5、实例更新之前: beforeUpdate');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
updated() {
console.groupCollapsed("%c%s", "color:red", '6、实例更新完毕:updated');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
beforeDestroy() {
console.groupCollapsed("%c%s", "color:red", '7、实例销毁之前:beforeDestroy');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
destroyed() {
console.groupCollapsed("%c%s", "color:red", '8、实例销毁完毕:destroyed');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
}
});
</script>
上面的例子中,如果在生命周期的钩子函数中打断点观察调用栈和作用域,更容易理解Vue的生命周期,在生命周期钩子中我们看到Vue内部执行了很多方法,其结果是实例的this在不断的变化。
生命周期的三个阶段
一、创建阶段
1.初始化事件和生命周期
实例、组件通过new Vue() 创建出来之后会初始化事件和生命周期
完成后,就会执行beforeCreate
钩子函数,这个时候,数据还没有挂载,无法访问到数据和真实的dom。
2. 初始化注入和校验
实例完成数据观测 (data observer), 属性和方法的运算,watch/event 事件回调。但挂载阶段还没开始,$el 属性目前不可见。
完成后,就会执行created
钩子函数,created函数是最早能够调用data和methods数据的地方,更改数据不会触发其他的钩子函数,一般可以在这里获取初始数据。
3. 找实例或者组件对应的模板,编译模板为虚拟Dom放入到render函数中准备渲染
① 判断实例里是否有el选项
- 存在el,实例将立即进入编译过程;
- 不存在el,需要显式调用 vm.$mount() 手动开启编译。
② 判断实例里是否有template选项
- 没有,将el挂载点的HTML作为template进行页面渲染;
- 有,使用实例里面的template进行页面渲染
完成后,就会执行beforeMount
钩子函数,这个函数中虚拟Dom已经创建完成,准备渲染,在这里也可以更改数据,不会触发数据更新钩子。
5. 开始render,渲染出真实dom
完成后,就会执行mounted
钩子函数,这个时候,组件已经出现在页面中,数据、真实dom都已经处理好了,事件都已经挂载好了。
二、运行阶段
-
如果组件或实例的数据被更改,会立即执行
beforeUpdate
钩子函数,钩子函数执行后后Vue的虚拟Dom机制会重新构建虚拟Dom与上一次的虚拟Dom树利用diff算法进行对比之后重新渲染。
-
当更新完成后,会执行
updated
钩子函数执行,此时数据已经更改完成,Dom也重新render完成。
三、销毁阶段
-
当经调用$destroy方法后,立即执行
beforeDestroy
钩子。 -
销毁实例与DOM之间的关联,解绑它的全部指令及事件监听器(并不能清除已有的页面上的DOM),之后会执行
destroyed
钩子。
beforeDestroy和destroyed用法相似,一般在这里做善后工作,如清除计时器、清除非指令绑定的事件等等
PS:销毁实例与DOM之间的关联,解绑它的全部指令及事件监听器,并不能清除已有的页面上的DOM。
vue中父级与子组件生命周期的先后顺序
修改一下上面的例子,在实例中挂载一个子组件,通过这段代码来看一下父级与子组件生命周期的先后顺序
<div id="app">
<input type="text" v-model="message">
<button v-on:click="handleDestroy">销毁父组件</button>
<button v-on:click="flag=!flag">销毁/重建子组件</button>
<child-a :msg="message" inline-template v-if="flag">
<div class="child-a">
<label>{{msg}}</label><input type="text" v-model="title">
</div>
</child-a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
message: '父组件信息',
flag: true
},
methods: {
handleDestroy() {
this.$destroy();
}
},
components: {
'childA': {
data() {
return {
title: '子组件',
}
},
props: ['msg'],
beforeCreate() {
console.log("%c%s", "color:green", '(1)、子组件 beforeCreate');
},
created() {
console.log("%c%s", "color:green", '(2)、子组件 created');
},
beforeMount() {
console.log("%c%s", "color:green", '(3)、子组件 beforeMount');
},
mounted() {
console.log("%c%s", "color:green", '(4)、子组件 mounted');
},
beforeUpdate() {
console.log("%c%s", "color:green", '(5)、子组件 beforeUpdate');
},
updated() {
console.log("%c%s", "color:green", '(6)、子组件 updated ');
},
beforeDestroy() {
console.log("%c%s", "color:green", '(7)、子组件 beforeDestroy ');
},
destroyed() {
console.log("%c%s", "color:green", '(8)、子组件 destroyed ');
}
}
},
beforeCreate() {
console.log("%c%s", "color:red", '1、实例创建之前: beforeCreate');
},
created() {
console.log("%c%s", "color:red", '2、实例创建完毕: created');
},
beforeMount() {
console.log("%c%s", "color:red", '3、实例挂载之前: beforeMount');
},
mounted() {
console.log("%c%s", "color:red", '4、实例挂载完毕: mounted');
},
beforeUpdate() {
console.log("%c%s", "color:red", '5、实例更新之前: beforeUpdate');
},
updated() {
console.log("%c%s", "color:red", '6、实例更新完毕:updated');
},
beforeDestroy() {
console.log("%c%s", "color:red", '7、实例销毁之前:beforeDestroy');
},
destroyed() {
console.log("%c%s", "color:red", '8、实例销毁完毕:destroyed');
}
});
</script>
1、当父组件执行完beforeMount
,会依次执行子组件的生命周期钩子,直到全部子组件挂载到实例上(执行完子组件的mounted钩子),父组件才会进入mounted钩子。
2、更新子组件的数据,不会触发父组件的生命周期钩子
3、如果子组件引用了父组件的数据,父组件数据更新时,执行顺序是:
4、销毁父组件时,同时销毁其包含的子组件,同前面说的一样,子组件的视图也不会消失
5、本例中,通过v-if的方法销毁子组件时,同时会触发父组件的更新钩子