Vue 生命周期
前言
在日常开发中,Vue 生命周期是使用频率最高的方法之一,而且在面试过程中也是出现比较高频的题目之一,因此我想从使用角度剖析它们。
生命周期示例
Vue2 生命周期图示参考官网
下面举个例子说明嵌套组件初始化,生命周期的执行顺序,部分组件使用了 element-ui 库:
/* App.vue 组件 */
<template>
<div class="page">
<p>我是父组件:{{ count }}</p>
<el-button type="primary" size="small" @click="onChange">点我+1</el-button>
<el-button type="primary" size="small" @click="onHide">点我隐藏子组件</el-button>
<el-divider />
<Child v-if="show" />
</div>
</template>
<script>
import { defineComponent } from 'vue'
import Child from '@/components/Child.vue'
export default defineComponent({
name: 'App',
components: {
Child,
},
data() {
return {
count: 0,
show: true,
}
},
beforeCreate() {
console.log('App beforeCreate')
},
created() {
console.log('App created')
},
beforeMount() {
console.log('App beforeMount')
},
mounted() {
console.log('App mounted')
},
beforeUpdate() {
console.log('App beforeUpdate')
},
updated() {
console.log('App updated')
},
beforeDestroy() {
console.log('App beforeDestroy')
},
destroyed() {
console.log('App destroyed')
},
methods: {
onChange() {
this.count++
},
onHide() {
this.show = !this.show
},
},
})
</script>
<style lang="scss" scoped></style>
点击查看 Child 组件代码
/* Child.vue 组件 */
<template>
<div class="page">
<p>我是子组件:{{ count }}</p>
<el-button type="primary" size="small" @click="onChange">点我+1</el-button>
<el-button type="primary" size="small" @click="onHide">点我隐藏孙组件</el-button>
<el-divider />
<Son v-if="show" />
</div>
</template>
<script>
import { defineComponent } from 'vue'
import Son from './Son.vue'
export default defineComponent({
name: 'ChildComp',
components: {
Son,
},
data() {
return {
count: 0,
show: true,
}
},
beforeCreate() {
console.log('Child beforeCreate')
},
created() {
console.log('Child created')
},
beforeMount() {
console.log('Child beforeMount')
},
mounted() {
console.log('Child mounted')
},
beforeUpdate() {
console.log('Child beforeUpdate')
},
updated() {
console.log('Child updated')
},
beforeDestroy() {
console.log('Child beforeDestroy')
},
destroyed() {
console.log('Child destroyed')
},
methods: {
onChange() {
this.count++
},
onHide() {
this.show = !this.show
},
},
})
</script>
<style lang="scss" scoped></style>
点击查看 Son 组件代码
/* Son.vue 组件 */
<template>
<div class="page">
<p>我是孙组件:{{ count }}</p>
<el-button type="primary" size="small" @click="onChange">点我+1</el-button>
</div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'SonComp',
data() {
return {
count: 0,
}
},
beforeCreate() {
console.log('Son beforeCreate')
},
created() {
console.log('Son created')
},
beforeMount() {
console.log('Son beforeMount')
},
mounted() {
console.log('Son mounted')
},
beforeUpdate() {
console.log('Son beforeUpdate')
},
updated() {
console.log('Son updated')
},
beforeDestroy() {
console.log('Son beforeDestroy')
},
destroyed() {
console.log('Son destroyed')
},
methods: {
onChange() {
this.count++
},
},
})
</script>
<style lang="scss" scoped></style>
浏览器控制台打印结果如下:
App beforeCreate
App created
App beforeMount
Child beforeCreate
Child created
Child beforeMount
Son beforeCreate
Son created
Son beforeMount
Son mounted
Child mounted
App mounted
由此得出结论:如果父组件有嵌套子组件,那么等待子组件完成mounted后才执行自己的生命周期钩子mounted。
通过子组件「点我+1」按钮操作页面响应式数据,浏览器控制台打印结果如下:
Child beforeUpdate
Child updated
由此得出结论:当属于该组件的响应式数据更改时,执行该组件的生命周期钩子beforeUpdate与updated。
通过父组件「点我隐藏子组件」按钮操作页面响应式数据,控制子组件隐藏(销毁),浏览器控制台打印结果如下:
App beforeUpdate
Child beforeDestroy
Son beforeDestroy
Son destroyed
Child destroyed
App updated
由此得出结论:当属于该组件的响应式数据更改时,执行该组件生命周期钩子beforeUpdate,然后是执行子组件的销毁生命周期钩子beforeDestroy,接着是子孙组件的销毁生命周期钩子beforeDestroy与destroyed,接着是子组件的销毁生命周期钩子destroyed,最后执行该组件的生命周期钩子updated。
总结
加载渲染过程如下:
- 父组件=>beforeCreate
- 父组件=>created
- 父组件=>beforeMount
- 子组件=>beforeCreate
- 子组件=>created
- 子组件=>beforeMount
- 子组件=>mounted
- 父组件=>mounted
更新过程:
- 父组件=>beforeUpdate
- 子组件=>beforeUpdate
- 子组件=>updated
- 父组件=>updated
销毁过程:
- 父组件=>beforeDestory
- 子组件=>beforeDestroy
- 子组件=>destroyed
- 父组件=>destroyed
keep-alive 内置组件新增如下两个生命周期:
- activated=>被 keep-alive 缓存的组件激活时调用
- deactivated=>被 keep-alive 缓存的组件失活时调用
一般在 created 生命周期执行异步请求数据,原因有如下两点:
- 能更快获取到服务端数据,减少页面加载时间,用户体验更好
- SSR 不支持 beforeMount 、mounted 钩子函数,放在 created 中保持一致性
