从创建到销毁,详解 Vue 组件的完整生命周期
Vue 的生命周期是指一个组件从创建、挂载、更新到销毁的整个过程。在这个过程中,Vue 会自动执行一些钩子函数,让我们可以在特定阶段编写自己的逻辑。理解生命周期是掌握 Vue 的基础。
一、Vue 2 vs Vue 3 生命周期对比
Vue 3 对生命周期做了一些调整,但核心概念不变。以下是两者的对比:
#mermaid-svg-CKp63wtAP64BzgIP{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CKp63wtAP64BzgIP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CKp63wtAP64BzgIP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CKp63wtAP64BzgIP .error-icon{fill:#552222;}#mermaid-svg-CKp63wtAP64BzgIP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CKp63wtAP64BzgIP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CKp63wtAP64BzgIP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CKp63wtAP64BzgIP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CKp63wtAP64BzgIP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CKp63wtAP64BzgIP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CKp63wtAP64BzgIP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CKp63wtAP64BzgIP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CKp63wtAP64BzgIP .marker.cross{stroke:#333333;}#mermaid-svg-CKp63wtAP64BzgIP svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CKp63wtAP64BzgIP p{margin:0;}#mermaid-svg-CKp63wtAP64BzgIP .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-CKp63wtAP64BzgIP .cluster-label text{fill:#333;}#mermaid-svg-CKp63wtAP64BzgIP .cluster-label span{color:#333;}#mermaid-svg-CKp63wtAP64BzgIP .cluster-label span p{background-color:transparent;}#mermaid-svg-CKp63wtAP64BzgIP .label text,#mermaid-svg-CKp63wtAP64BzgIP span{fill:#333;color:#333;}#mermaid-svg-CKp63wtAP64BzgIP .node rect,#mermaid-svg-CKp63wtAP64BzgIP .node circle,#mermaid-svg-CKp63wtAP64BzgIP .node ellipse,#mermaid-svg-CKp63wtAP64BzgIP .node polygon,#mermaid-svg-CKp63wtAP64BzgIP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CKp63wtAP64BzgIP .rough-node .label text,#mermaid-svg-CKp63wtAP64BzgIP .node .label text,#mermaid-svg-CKp63wtAP64BzgIP .image-shape .label,#mermaid-svg-CKp63wtAP64BzgIP .icon-shape .label{text-anchor:middle;}#mermaid-svg-CKp63wtAP64BzgIP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CKp63wtAP64BzgIP .rough-node .label,#mermaid-svg-CKp63wtAP64BzgIP .node .label,#mermaid-svg-CKp63wtAP64BzgIP .image-shape .label,#mermaid-svg-CKp63wtAP64BzgIP .icon-shape .label{text-align:center;}#mermaid-svg-CKp63wtAP64BzgIP .node.clickable{cursor:pointer;}#mermaid-svg-CKp63wtAP64BzgIP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CKp63wtAP64BzgIP .arrowheadPath{fill:#333333;}#mermaid-svg-CKp63wtAP64BzgIP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CKp63wtAP64BzgIP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CKp63wtAP64BzgIP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CKp63wtAP64BzgIP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CKp63wtAP64BzgIP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CKp63wtAP64BzgIP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CKp63wtAP64BzgIP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CKp63wtAP64BzgIP .cluster text{fill:#333;}#mermaid-svg-CKp63wtAP64BzgIP .cluster span{color:#333;}#mermaid-svg-CKp63wtAP64BzgIP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CKp63wtAP64BzgIP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CKp63wtAP64BzgIP rect.text{fill:none;stroke-width:0;}#mermaid-svg-CKp63wtAP64BzgIP .icon-shape,#mermaid-svg-CKp63wtAP64BzgIP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CKp63wtAP64BzgIP .icon-shape p,#mermaid-svg-CKp63wtAP64BzgIP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CKp63wtAP64BzgIP .icon-shape rect,#mermaid-svg-CKp63wtAP64BzgIP .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CKp63wtAP64BzgIP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CKp63wtAP64BzgIP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CKp63wtAP64BzgIP :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Vue3
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeUnmount
unmounted
Vue2
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeDestroy
destroyed
| beforeCreate | beforeCreate | 组件实例刚创建 |
| created | created | 数据观测完成 |
| beforeMount | beforeMount | 模板编译完成,还未挂载 |
| mounted | mounted | 挂载到 DOM |
| beforeUpdate | beforeUpdate | 数据更新前 |
| updated | updated | 数据更新完成 |
| beforeDestroy | beforeUnmount | 组件销毁前 |
| destroyed | unmounted | 组件已销毁 |
Vue 3 主要的变化是将 destroy 改为 unmount,语义更清晰。
二、各生命周期详解
2.1 beforeCreate
<script>
export default {
beforeCreate() {
console.log('beforeCreate – 实例创建前');
// 此时 data、methods 还未初始化
console.log(this.$data); // undefined
}
}
</script>
执行时机:组件实例刚创建,还没有任何响应式数据。
适用场景:
- 初始化非响应式的数据
- 性能监控埋点
注意:这个阶段几乎不用,因为什么都还没准备好。
2.2 created
<script>
export default {
data() {
return { message: 'Hello' }
},
created() {
console.log('created – 实例创建完成');
// data 已经可用
console.log(this.message); // 'Hello'
// 可以在此时发起接口请求
this.fetchData();
},
methods: {
async fetchData() {
const res = await fetch('/api/data');
this.data = await res.json();
}
}
}
</script>
执行时机:实例已完成数据观测(data)、方法(methods)的绑定。
适用场景:
- 发起初始接口请求
- 初始化组件状态
- 进行数据预处理
这是最常用的生命周期之一,常用于组件的初始化。
2.3 beforeMount
<script>
export default {
beforeMount() {
console.log('beforeMount – 挂载前');
// 模板已经编译完成,但还未渲染到 DOM
console.log(this.$el); // undefined
// 可以访问 this.$refs(但此时还是空对象)
console.log(this.$refs.myDiv); // undefined
}
}
</script>
<template>
<div ref="myDiv">Hello</div>
</template>
执行时机:模板已经编译完成,生成 render 函数,但还未创建 DOM 节点。
适用场景:
- 几乎不用
- 某些需要提前获取 DOM 信息的场景(但不推荐)
2.4 mounted
<script>
export default {
mounted() {
console.log('mounted – 挂载完成');
// DOM 已经渲染完成
console.log(this.$refs.myDiv); // <div>Hello</div>
// 第三方库初始化
this.initChart();
this.initEventListeners();
},
methods: {
initChart() {
// ECharts 初始化需要 DOM
this.chart = echarts.init(this.$refs.chart);
}
}
}
</script>
<template>
<div ref="chart" style="width: 100%; height: 400px;"></div>
</template>
执行时机:组件已经挂载到 DOM,DOM 节点已创建并可访问。
适用场景:
- 第三方库初始化(ECharts、Swiper 等)
- DOM 操作
- 绑定事件监听器
- 发起需要 DOM 的初始化请求
这是最常用的生命周期,用于需要操作 DOM 的场景。
2.5 beforeUpdate
<script>
export default {
data() {
return { count: 0 }
},
beforeUpdate() {
console.log('beforeUpdate – 更新前');
// 可以在更新前获取更新前的 DOM 状态
console.log(this.$refs.counter.textContent); // 旧值
// 谨慎使用:这里修改数据会导致死循环
},
methods: {
increment() {
this.count++;
}
}
}
</script>
<template>
<button ref="counter" @click="increment">{{ count }}</button>
</template>
执行时机:数据发生变化,但 DOM 还未更新。
适用场景:
- 获取更新前的 DOM 状态
- 在 DOM 更新前移除事件监听
- 配合 updated 做性能优化
2.6 updated
<script>
export default {
data() {
return { count: 0 }
},
updated() {
console.log('updated – 更新完成');
// DOM 已经更新完成
console.log(this.$refs.counter.textContent); // 新值
// 重新计算基于 DOM 的数据
this.recalculatePosition();
},
methods: {
recalculatePosition() {
// 基于新的 DOM 状态做处理
}
}
}
</script>
执行时机:DOM 已更新完成。
适用场景:
- 基于最新 DOM 做处理
- 重新计算布局
- 触发基于更新的动画
注意:避免在 updated 中修改 data,否则会触发死循环。
2.7 beforeUnmount(Vue 3)/ beforeDestroy(Vue 2)
<script>
export default {
beforeUnmount() {
console.log('beforeUnmount – 销毁前');
// 清理定时器
clearInterval(this.timer);
clearTimeout(this.timeout);
// 移除事件监听
window.removeEventListener('resize', this.handleResize);
// 取消未完成的请求
this.controller?.abort();
},
mounted() {
this.timer = setInterval(() => {
this.fetchData();
}, 5000);
window.addEventListener('resize', this.handleResize);
}
}
</script>
执行时机:组件即将被销毁,但 DOM 还存在。
适用场景:
- 清理定时器
- 移除事件监听
- 取消未完成的网络请求
- 清理 Vuex 或 Pinia 订阅
2.8 unmounted(Vue 3)/ destroyed(Vue 2)
<script>
export default {
unmounted() {
console.log('unmounted – 销毁完成');
// 理论上这里不应该再有 DOM 操作
// 但某些情况下可能还能访问到
// 可以做最终的清理工作
this.cleanup();
}
}
</script>
执行时机:组件已销毁,DOM 已移除。
适用场景:
- 几乎不用
- 调试用
三、Composition API 中的生命周期
Vue 3 的 Composition API 使用 setup 函数,生命周期钩子需要通过 import 引入:
<script setup>
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
// 等同于 beforeCreate + created
const message = ref('Hello')
onBeforeMount(() => {
console.log('onBeforeMount')
})
onMounted(() => {
console.log('onMounted')
// 第三方库初始化
echarts.init(document.querySelector('.chart'))
})
onBeforeUpdate(() => {
console.log('onBeforeUpdate')
})
onUpdated(() => {
console.log('onUpdated')
})
onBeforeUnmount(() => {
// 清理定时器
clearInterval(timer)
})
onUnmounted(() => {
console.log('onUnmounted')
})
</script>
| beforeCreate | setup(直接写) |
| created | setup(直接写) |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeUnmount | onBeforeUnmount |
| unmounted | onUnmounted |
四、生命周期实际应用
4.1 接口请求
<script>
export default {
data() {
return {
list: [],
loading: false
}
},
// 推荐:created 中发起请求
created() {
this.fetchList();
},
// 或者:mounted 中发起请求(需要 DOM 时用)
mounted() {
this.initChart();
},
methods: {
async fetchList() {
this.loading = true;
try {
const res = await fetch('/api/list');
this.list = await res.json();
} finally {
this.loading = false;
}
}
}
}
</script>
4.2 第三方库初始化
<script>
import echarts from 'echarts'
export default {
mounted() {
// ECharts 需要 DOM
this.chart = echarts.init(this.$refs.chart)
this.chart.setOption({
title: { text: 'ECharts' },
series: [{ type: 'pie', data: [1, 2, 3] }]
})
// 监听窗口变化
window.addEventListener('resize', this.handleResize)
},
beforeUnmount() {
// 清理
window.removeEventListener('resize', this.handleResize)
this.chart?.dispose()
},
methods: {
handleResize() {
this.chart?.resize()
}
}
}
</script>
<template>
<div ref="chart" style="width: 100%; height: 300px;"></div>
</template>
4.3 定时器管理
<script>
export default {
data() {
return {
count: 0,
timer: null
}
},
mounted() {
// 启动定时器
this.timer = setInterval(() => {
this.count++;
}, 1000);
},
beforeUnmount() {
// 清理定时器(必须!)
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
}
</script>
4.4 路由守卫配合
<script>
export default {
// 每次进入组件都会执行
created() {
// 从缓存页面回来时,created 不会再次执行
// 使用 watch 监听 $route 可以解决这个问题
this.fetchData();
},
watch: {
$route(to, from) {
// 路由参数变化时重新获取数据
if (to.params.id !== from.params.id) {
this.fetchData();
}
}
},
methods: {
fetchData() {
console.log('fetch data…');
}
}
}
</script>
五、KeepAlive 缓存与生命周期
使用 <keep-alive> 缓存组件时,生命周期会有所变化:
<!– 父组件 –>
<template>
<keep-alive :include="['UserList']">
<router-view />
</keep-alive>
</template>
#mermaid-svg-3yocIahAft2FyGoA{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3yocIahAft2FyGoA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3yocIahAft2FyGoA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3yocIahAft2FyGoA .error-icon{fill:#552222;}#mermaid-svg-3yocIahAft2FyGoA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3yocIahAft2FyGoA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3yocIahAft2FyGoA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3yocIahAft2FyGoA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3yocIahAft2FyGoA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3yocIahAft2FyGoA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3yocIahAft2FyGoA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3yocIahAft2FyGoA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3yocIahAft2FyGoA .marker.cross{stroke:#333333;}#mermaid-svg-3yocIahAft2FyGoA svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3yocIahAft2FyGoA p{margin:0;}#mermaid-svg-3yocIahAft2FyGoA .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-3yocIahAft2FyGoA .cluster-label text{fill:#333;}#mermaid-svg-3yocIahAft2FyGoA .cluster-label span{color:#333;}#mermaid-svg-3yocIahAft2FyGoA .cluster-label span p{background-color:transparent;}#mermaid-svg-3yocIahAft2FyGoA .label text,#mermaid-svg-3yocIahAft2FyGoA span{fill:#333;color:#333;}#mermaid-svg-3yocIahAft2FyGoA .node rect,#mermaid-svg-3yocIahAft2FyGoA .node circle,#mermaid-svg-3yocIahAft2FyGoA .node ellipse,#mermaid-svg-3yocIahAft2FyGoA .node polygon,#mermaid-svg-3yocIahAft2FyGoA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3yocIahAft2FyGoA .rough-node .label text,#mermaid-svg-3yocIahAft2FyGoA .node .label text,#mermaid-svg-3yocIahAft2FyGoA .image-shape .label,#mermaid-svg-3yocIahAft2FyGoA .icon-shape .label{text-anchor:middle;}#mermaid-svg-3yocIahAft2FyGoA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3yocIahAft2FyGoA .rough-node .label,#mermaid-svg-3yocIahAft2FyGoA .node .label,#mermaid-svg-3yocIahAft2FyGoA .image-shape .label,#mermaid-svg-3yocIahAft2FyGoA .icon-shape .label{text-align:center;}#mermaid-svg-3yocIahAft2FyGoA .node.clickable{cursor:pointer;}#mermaid-svg-3yocIahAft2FyGoA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3yocIahAft2FyGoA .arrowheadPath{fill:#333333;}#mermaid-svg-3yocIahAft2FyGoA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3yocIahAft2FyGoA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3yocIahAft2FyGoA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3yocIahAft2FyGoA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3yocIahAft2FyGoA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3yocIahAft2FyGoA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3yocIahAft2FyGoA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3yocIahAft2FyGoA .cluster text{fill:#333;}#mermaid-svg-3yocIahAft2FyGoA .cluster span{color:#333;}#mermaid-svg-3yocIahAft2FyGoA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3yocIahAft2FyGoA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3yocIahAft2FyGoA rect.text{fill:none;stroke-width:0;}#mermaid-svg-3yocIahAft2FyGoA .icon-shape,#mermaid-svg-3yocIahAft2FyGoA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3yocIahAft2FyGoA .icon-shape p,#mermaid-svg-3yocIahAft2FyGoA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3yocIahAft2FyGoA .icon-shape rect,#mermaid-svg-3yocIahAft2FyGoA .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3yocIahAft2FyGoA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3yocIahAft2FyGoA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3yocIahAft2FyGoA :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
再次进入
离开缓存
首次进入
首次进入
onMounted
onActivated
onActivated(不触发 mounted)
<script>
export default {
// 第一次进入
mounted() {
console.log('mounted – 首次挂载');
this.fetchData();
},
// 被缓存的组件激活时
activated() {
console.log('activated – 组件激活');
// 每次从缓存回来都会执行
this.refreshData();
},
// 被缓存的组件停用时
deactivated() {
console.log('deactivated – 组件停用');
// 组件被缓存时执行
}
}
</script>
| mounted | 首次渲染时执行 |
| activated | 每次从缓存激活时执行 |
| deactivated | 组件被缓存时执行 |
适用场景:
- 列表页缓存,返回时保留滚动位置
- 表单页缓存,返回时不丢失填写数据
六、父子组件生命周期执行顺序
6.1 创建过程
子组件父组件子组件父组件#mermaid-svg-uvTaAYqoeZ9FYg1z{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uvTaAYqoeZ9FYg1z .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uvTaAYqoeZ9FYg1z .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uvTaAYqoeZ9FYg1z .error-icon{fill:#552222;}#mermaid-svg-uvTaAYqoeZ9FYg1z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uvTaAYqoeZ9FYg1z .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uvTaAYqoeZ9FYg1z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uvTaAYqoeZ9FYg1z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uvTaAYqoeZ9FYg1z .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uvTaAYqoeZ9FYg1z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uvTaAYqoeZ9FYg1z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uvTaAYqoeZ9FYg1z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uvTaAYqoeZ9FYg1z .marker.cross{stroke:#333333;}#mermaid-svg-uvTaAYqoeZ9FYg1z svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uvTaAYqoeZ9FYg1z p{margin:0;}#mermaid-svg-uvTaAYqoeZ9FYg1z .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uvTaAYqoeZ9FYg1z text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-uvTaAYqoeZ9FYg1z .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-uvTaAYqoeZ9FYg1z .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-uvTaAYqoeZ9FYg1z .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-uvTaAYqoeZ9FYg1z .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-uvTaAYqoeZ9FYg1z #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-uvTaAYqoeZ9FYg1z .sequenceNumber{fill:white;}#mermaid-svg-uvTaAYqoeZ9FYg1z #sequencenumber{fill:#333;}#mermaid-svg-uvTaAYqoeZ9FYg1z #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-uvTaAYqoeZ9FYg1z .messageText{fill:#333;stroke:none;}#mermaid-svg-uvTaAYqoeZ9FYg1z .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uvTaAYqoeZ9FYg1z .labelText,#mermaid-svg-uvTaAYqoeZ9FYg1z .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-uvTaAYqoeZ9FYg1z .loopText,#mermaid-svg-uvTaAYqoeZ9FYg1z .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-uvTaAYqoeZ9FYg1z .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-uvTaAYqoeZ9FYg1z .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-uvTaAYqoeZ9FYg1z .noteText,#mermaid-svg-uvTaAYqoeZ9FYg1z .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-uvTaAYqoeZ9FYg1z .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uvTaAYqoeZ9FYg1z .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uvTaAYqoeZ9FYg1z .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uvTaAYqoeZ9FYg1z .actorPopupMenu{position:absolute;}#mermaid-svg-uvTaAYqoeZ9FYg1z .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-uvTaAYqoeZ9FYg1z .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uvTaAYqoeZ9FYg1z .actor-man circle,#mermaid-svg-uvTaAYqoeZ9FYg1z line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-uvTaAYqoeZ9FYg1z :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}beforeCreatecreatedbeforeMountbeforeCreatecreatedbeforeMountmountedmounted
// 父组件
created() { console.log('父 created') }
mounted() { console.log('父 mounted') }
// 子组件
created() { console.log('子 created') }
mounted() { console.log('子 mounted') }
// 输出顺序:
// 父 created
// 子 created
// 子 mounted
// 父 mounted
6.2 更新过程
// 父组件
beforeUpdate() { console.log('父 beforeUpdate') }
updated() { console.log('父 updated') }
// 子组件
beforeUpdate() { console.log('子 beforeUpdate') }
updated() { console.log('子 updated') }
// 当父组件数据变化触发更新:
// 父 beforeUpdate
// 子 beforeUpdate
// 子 updated
// 父 updated
6.3 销毁过程
// 父组件
beforeUnmount() { console.log('父 beforeUnmount') }
unmounted() { console.log('父 unmounted') }
// 子组件
beforeUnmount() { console.log('子 beforeUnmount') }
unmounted() { console.log('子 unmounted') }
// 销毁父组件时:
// 父 beforeUnmount
// 子 beforeUnmount
// 子 unmounted
// 父 unmounted
七、常见问题
7.1 created 和 mounted 有什么区别?
| DOM | 不可访问 | 可访问 |
| 数据 | 已响应式 | 已响应式 |
| 适用 | 接口请求 | DOM 操作 |
简单理解:需要操作 DOM 用 mounted,只需要数据用 created。
7.2 为什么定时器要在 beforeUnmount 清理?
<!– 不清理的后果 –>
<template>
<button @click="show = !show">切换</button>
<Child v-if="show" />
</template>
<script>
export default {
components: { Child },
data() { return { show: true } }
}
</script>
// Child.vue
mounted() {
this.timer = setInterval(() => {
console.log('timer running');
}, 1000);
}
// 切换 v-if 时,组件被销毁
// 如果不清理定时器,定时器还在运行
// 会导致内存泄漏和意外行为
7.3 为什么 updated 中修改数据会死循环?
updated() {
// 错误示例
this.count++; // 这会再次触发 updated
// 死循环!
}
// 正确做法:使用 watch 监听变化
watch: {
count(newVal) {
// 处理变化
}
}
7.4 异步组件的生命周期
<script>
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
</script>
<template>
<AsyncComponent v-if="show" />
<button @click="show = true">加载</button>
</template>
异步组件的生命周期:
- 加载中:触发 loading 钩子
- 加载成功:触发 resolved 钩子
- 加载失败:触发 error 钩子
- 实际组件:正常生命周期
八、总结
Vue 生命周期是开发中非常重要的概念,核心要点:
理解生命周期,能够帮助我们:
- 在正确的时机做正确的事
- 避免内存泄漏
- 优化性能
- 解决奇怪的问题



