前言
生命周期是每个 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) })
onRenderTriggered((e) => { console.log('triggered 事件对象:', e) })
const increment = () => { count.value++ }
|
这两个钩子主要用于调试性能问题,配合 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 生成,仅供参考。本文发布于 码上学习,转载请注明出处。