Vue3的生命周期

前言

生命周期是每个 Vue 开发者必须掌握的基础。Vue3 在 Vue2 基础上做了命名调整并新增了调试钩子,同时 Composition API 的写法也让生命周期有了新的使用方式。

一、生命周期全景图

1
2
3
4
5
6
7
8
创建阶段                    挂载阶段                    更新阶段                    卸载阶段
│ │ │ │
setup() onBeforeMount onBeforeUpdate onBeforeUnmount
│ │ │ │
│ ┌──────────────────► │ ┌─────────────────► │ ┌────────────────► │
│ │ │ │ │ │ │
▼ │ ▼ │ ▼ │ ▼
(编译) onMounted onUpdated onUnmounted

执行顺序总结:

1
setup() → onBeforeMount → onMounted → onBeforeUpdate → onUpdated → onBeforeUnmount → onUnmounted

二、Options API vs Composition API 对照

Vue2 Options Vue3 Options Vue3 Composition 说明
beforeCreate beforeCreate setup() 本身 此时 data 和 methods 不可用
created created setup() 本身 可访问响应式数据,无 DOM
beforeMount beforeMount onBeforeMount 首次渲染前
mounted mounted onMounted DOM 已挂载
beforeUpdate beforeUpdate onBeforeUpdate 数据变化,DOM 更新前
updated updated onUpdated 数据变化,DOM 更新后
beforeDestroy beforeUnmount onBeforeUnmount 组件卸载前
destroyed unmounted onUnmounted 组件卸载后
❌ 不存在 ❌ 不存在 onRenderTracked 🆕 调试:依赖被追踪时
❌ 不存在 ❌ 不存在 onRenderTriggered 🆕 调试:依赖触发更新时
❌ 不存在 ❌ 不存在 onActivated keep-alive 激活
❌ 不存在 ❌ 不存在 onDeactivated keep-alive 停用
❌ 不存在 ❌ 不存在 onServerPrefetch 🆕 SSR 服务端预取

关键变更:destroyed → unmounted,beforeDestroy → beforeUnmount,命名更语义化。

三、常用钩子详解

setup() — 最早执行

1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup>
// <script setup> 中的顶层代码 = setup() 函数体
import { ref, onMounted } from 'vue'

const count = ref(0)
// 这里就是 beforeCreate + created 阶段
// 此时响应式数据已可用,但没有 DOM

onMounted(() => {
console.log('DOM 已挂载')
console.log(document.querySelector('.box')) // 可以拿到 DOM
})
</script>

onMounted — 初始化数据/操作 DOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script setup>
import { ref, onMounted } from 'vue'

const list = ref([])
const loading = ref(true)

// 请求数据、初始化图表、注册事件监听等
onMounted(async () => {
list.value = await fetchList()
loading.value = false

// 初始化 ECharts
const chart = echarts.init(document.getElementById('chart'))
chart.setOption(options)
})
</script>

onBeforeUnmount — 清理副作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script setup>
import { onMounted, onBeforeUnmount } from 'vue'

let timer = null
let resizeHandler = null

onMounted(() => {
// 定时器
timer = setInterval(() => console.log('tick'), 1000)

// 事件监听
resizeHandler = () => console.log('resize')
window.addEventListener('resize', resizeHandler)
})

onBeforeUnmount(() => {
// 清理定时器
clearInterval(timer)

// 移除事件监听(避免内存泄漏)
window.removeEventListener('resize', resizeHandler)
})
</script>

onBeforeUnmount 是写清理逻辑的标准位置,定时器、事件监听、WebSocket 连接等都要在这里断开。

四、父子组件生命周期顺序

挂载阶段

1
2
3
父 setup() → 父 onBeforeMount
→ 子 setup() → 子 onBeforeMount → 子 onMounted
→ 父 onMounted
1
2
3
4
5
6
7
8
9
10
11
<!-- 父组件 Parent.vue -->
<script setup>
import { onBeforeMount, onMounted } from 'vue'
import Child from './Child.vue'

console.log('父 setup')
onBeforeMount(() => console.log('父 onBeforeMount'))
onMounted(() => console.log('父 onMounted'))
</script>

<template><Child /></template>
1
2
3
4
5
6
7
8
<!-- 子组件 Child.vue -->
<script setup>
import { onBeforeMount, onMounted } from 'vue'

console.log('子 setup')
onBeforeMount(() => console.log('子 onBeforeMount'))
onMounted(() => console.log('子 onMounted'))
</script>

输出结果:

1
2
3
4
5
6
父 setup
父 onBeforeMount
子 setup
子 onBeforeMount
子 onMounted ← 子组件先挂载完成
父 onMounted ← 父组件后挂载完成(等所有子组件挂载完)

更新阶段

1
2
3
4
父 onBeforeUpdate
子 onBeforeUpdate
子 onUpdated ← 子先更新完
父 onUpdated

卸载阶段

1
2
3
4
父 onBeforeUnmount
子 onBeforeUnmount
子 onUnmounted ← 子先卸载
父 onUnmounted

规律:子组件在父组件的挂载/更新/卸载过程中始终”先完成”。父组件要等所有子组件处理完毕后自己才算真正完成。

五、新增调试钩子

Vue3 新增两个仅 Composition API 可用的调试钩子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { ref, onRenderTracked, onRenderTriggered } from 'vue'

const count = ref(0)

// 每当响应式依赖被追踪时触发(首次渲染时记录所有依赖)
onRenderTracked((e) => {
console.log('tracked 事件对象:', e)
// e.target — 追踪的响应式对象
// e.key — 追踪的属性
// e.type — 'get' / 'has' / 'iterate'
// e.effect — 当前的副作用函数
})

// 每当响应式依赖变化触发重新渲染时触发
onRenderTriggered((e) => {
console.log('triggered 事件对象:', e)
// e.target — 变化的响应式对象
// e.key — 变化的属性
// e.newValue — 新值
// e.oldValue — 旧值
// e.type — 'set' / 'add' / 'delete' / 'clear'
})

const increment = () => {
count.value++ // 触发 onRenderTriggered
}

这两个钩子主要用于调试性能问题,配合 Vue DevTools 定位不必要的渲染。不要在 hook 中修改响应式数据,会导致死循环。

六、keep-alive 专属钩子

<KeepAlive> 包裹的组件有缓存激活/停用的独立生命周期:

1
2
3
4
5
6
<!-- App.vue -->
<template>
<KeepAlive>
<component :is="currentTab" />
</KeepAlive>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 被缓存的组件 -->
<script setup>
import { onActivated, onDeactivated } from 'vue'

// 组件从缓存中激活(每次切入都会触发)
onActivated(() => {
console.log('组件被激活了,可以刷新数据')
fetchData()
})

// 组件被放入缓存
onDeactivated(() => {
console.log('组件被缓存了,清理定时器')
clearInterval(timer)
})
</script>

完整的 keep-alive 生命周期流程:

1
2
3
4
首次进入:setup() → onBeforeMount → onMounted → onActivated
切出: onDeactivated
切回: onActivated(不会触发 mount)
最终销毁: onBeforeUnmount → onUnmounted

onActivated / onDeactivated 必须配合 <KeepAlive> 使用,否则不会触发。

七、Composition API 多个钩子

同一生命周期可以注册多个回调,按注册顺序执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script setup>
import { onMounted } from 'vue'

// 可以注册多个,按顺序执行
onMounted(() => console.log('第 1 个'))
onMounted(() => console.log('第 2 个'))
onMounted(() => console.log('第 3 个'))
// 输出:第 1 个 → 第 2 个 → 第 3 个

// 可以在 async 函数内使用(注意执行时机)
async function init() {
await loadConfig()
onMounted(() => {
// 即使 onMounted 注册得晚,挂载时仍会执行
console.log('异步注册的 onMounted')
})
}
init()
</script>

八、实战场景汇总

场景 适用钩子 说明
请求初始数据 onMounted DOM 已挂载,可拿到 ref
操作 DOM / 初始化图表 onMounted 通过 $refs 或 document.querySelector
注册全局事件 onMounted scroll、resize、keydown
启动定时器 onMounted setInterval、requestAnimationFrame
清理副作用 onBeforeUnmount 定时器、事件、WebSocket、Observer
路由参数变化刷新 watch(() => route.params.id, fn) 组件复用时不走 mount,用 watch
keep-alive 刷新数据 onActivated 切回页面时更新
调试追踪 onRenderTracked / onRenderTriggered 定位不必要渲染
获取上级 provide setup 阶段 inject 必须在 setup 同步调用

总结

  • Vue3 生命周期基本对应 Vue2,命名更规范(destroyed → unmounted)
  • setup() 替代了 beforeCreate 和 created,且是同步执行
  • 父组件挂载完成前会等所有子组件先完成
  • onBeforeUnmount 是写清理逻辑的标准位置,不要遗漏
  • onRenderTracked / onRenderTriggered 是 Vue3 独有的调试利器
  • Composition API 允许同一钩子注册多个回调,按序执行
  • onActivated / onDeactivated 需配合 <KeepAlive> 使用

内容由 AI 生成,仅供参考。本文发布于 码上学习,转载请注明出处。