第4章 Vue3生命周期:生命周期钩子函数详解与实战应用
生命周期,说白了就是组件从生到死的全过程。Vue3 把这一套流程分成了几个关键节点,每个节点都给你留了一个“钩子”,让你能插进去干点私活。我个人习惯把生命周期理解成“组件的成长日记”——什么时候出生、什么时候长身体、什么时候干活、什么时候消亡,清清楚楚。
4.1 生命周期整体流程
先看一张总图,心里有个谱。Vue3 的生命周期主要分三个阶段:
- 创建阶段:组件实例被创建,数据初始化
- 挂载阶段:组件渲染到 DOM 上
- 卸载阶段:组件从 DOM 上移除
嗯,这里要注意,Vue3 的 Composition API 里,生命周期钩子都改成了 onXxx 的形式。和 Vue2 的 created、mounted 写法不一样了。你想想看,为什么改?说白了是为了更好的逻辑复用和类型推导。
4.2 setup 函数——一切开始的地方
setup 是 Composition API 的入口。它在组件创建之前执行,比 beforeCreate 还早。我在项目中遇到过不少新手把 setup 当成 created 来用,其实不完全对。
import { ref, onMounted } from 'vue'
export default {
setup() {
const count = ref(0)
console.log('setup 执行了,此时组件还没挂载')
onMounted(() => {
console.log('组件挂载完成')
})
return { count }
}
}
核心要点:setup 中不能访问 this,因为组件实例还没完全创建好。所有响应式数据、方法、生命周期钩子都在这里定义。
4.3 常用的生命周期钩子
Vue3 提供了 8 个主要钩子,我挑几个最常用的给你讲讲。
| 钩子函数 | 触发时机 | 常见用途 |
|---|---|---|
onBeforeMount |
组件挂载到 DOM 之前 | 最后一次修改数据的机会 |
onMounted |
组件挂载完成后 | 发起 API 请求、操作 DOM |
onBeforeUpdate |
数据更新、DOM 重新渲染之前 | 获取更新前的 DOM 状态 |
onUpdated |
数据更新、DOM 重新渲染之后 | 执行依赖 DOM 的操作 |
onBeforeUnmount |
组件卸载之前 | 清理定时器、取消订阅 |
onUnmounted |
组件卸载之后 | 收尾工作 |
4.4 onMounted——最常用的钩子
我个人觉得 onMounted 是使用频率最高的钩子。为什么?因为这时候 DOM 已经渲染好了,你可以放心地操作它。
import { onMounted, ref } from 'vue'
export default {
setup() {
const userList = ref([])
onMounted(async () => {
// 发起数据请求
const res = await fetch('/api/users')
userList.value = await res.json()
})
return { userList }
}
}
小技巧:onMounted 里不要做太重的同步操作,否则会阻塞渲染。异步请求放在这里是最合适的。
4.5 onUnmounted——别忘了清理现场
我曾经在项目中犯过一个低级错误:在 onMounted 里开了个定时器,结果组件卸载了定时器还在跑。页面切走了,控制台还在疯狂输出日志。嗯,从那以后我养成了习惯——所有副作用都要在 onUnmounted 里清理干净。
import { onMounted, onUnmounted, ref } from 'vue'
export default {
setup() {
const timer = ref(null)
onMounted(() => {
timer.value = setInterval(() => {
console.log('心跳检测中...')
}, 1000)
})
onUnmounted(() => {
if (timer.value) {
clearInterval(timer.value)
timer.value = null
}
})
}
}
避坑指南:我曾经见过有人把 addEventListener 加在 onMounted 里,但忘了在 onUnmounted 里 removeEventListener。结果就是内存泄漏,页面越用越卡。记住:谁污染谁治理。
4.6 onBeforeUnmount——最后的抢救机会
这个钩子触发时,组件还在 DOM 上,但马上就要被移除了。你可以在这里做一些“临终关怀”。比如保存草稿、记录用户行为等。
import { onBeforeUnmount } from 'vue'
export default {
setup() {
onBeforeUnmount(() => {
// 保存用户未提交的表单数据
localStorage.setItem('draft', JSON.stringify(formData))
console.log('草稿已保存')
})
}
}
4.7 生命周期实战——一个完整的例子
光说不练假把式。我们写一个真实场景:一个实时数据监控面板。组件挂载时开始监听,卸载时停止监听。
import { ref, onMounted, onUnmounted } from 'vue'
export default {
setup() {
const cpuUsage = ref(0)
const memoryUsage = ref(0)
let ws = null
const connectWebSocket = () => {
ws = new WebSocket('ws://monitor.example.com')
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
cpuUsage.value = data.cpu
memoryUsage.value = data.memory
}
}
const disconnectWebSocket = () => {
if (ws) {
ws.close()
ws = null
}
}
onMounted(() => {
connectWebSocket()
})
onUnmounted(() => {
disconnectWebSocket()
})
return { cpuUsage, memoryUsage }
}
}
实战要点:WebSocket 连接一定要在 onUnmounted 里关闭。否则页面切走了,连接还在,服务器还在给你推送数据,白白浪费带宽。
4.8 生命周期执行顺序
如果你在父组件里嵌套了子组件,生命周期的执行顺序是这样的:
- 父组件
setup - 父组件
onBeforeMount - 子组件
setup - 子组件
onBeforeMount - 子组件
onMounted - 父组件
onMounted
为什么会这样?因为父组件要等所有子组件都挂载完了,自己才算挂载完成。这个顺序在写复杂组件时特别重要。我记得有一次调试一个弹窗组件,子组件的数据一直拿不到,后来才发现是生命周期顺序搞反了。
4.9 总结
生命周期是 Vue 组件的骨架。掌握了它,你就知道什么时候该做什么事。我个人建议:
- 数据请求放
onMounted - 副作用清理放
onUnmounted - DOM 操作放
onMounted或onUpdated - 初始化逻辑放
setup
记住一句话:谁创建谁销毁,谁订阅谁取消。做到这四点,生命周期这块基本就稳了。
下一章我们会讲 Vue3 的响应式原理,看看 ref 和 reactive 到底是怎么工作的。到时候你会发现,理解了生命周期,响应式就更好理解了。