欢迎光临
我们一直在努力

Vue 生命周期完全指南

从创建到销毁,详解 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

Vue 2Vue 3说明
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>

选项 APIComposition API
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 有什么区别?

阶段createdmounted
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 生命周期是开发中非常重要的概念,核心要点:

  • created:最适合发起初始请求的时机
  • mounted:需要操作 DOM 时的首选
  • beforeUnmount:清理定时器、事件监听、取消请求
  • keep-alive:使用 activated/deactivated 管理缓存组件
  • 理解生命周期,能够帮助我们:

    • 在正确的时机做正确的事
    • 避免内存泄漏
    • 优化性能
    • 解决奇怪的问题
    赞(0)
    未经允许不得转载:171主机测评 » Vue 生命周期完全指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址