0赞
赏
赞赏
更多好文
一张图理清变迁,三案例掌握迁移,告别“钩子迷航”
🌱 引言:为什么生命周期是 Vue 开发的“导航仪”?
生命周期钩子是 Vue 组件从诞生到消亡的“心跳节拍”。掌握它,你才能:
✅ 精准发起 API 请求
✅ 安全操作 DOM 元素
✅ 避免内存泄漏陷阱
✅ 高效组织复杂逻辑
而 Vue 3 带来的 Composition API 与术语革新,让许多开发者陷入“钩子迷航”。本文以对比图谱+迁移指南+实战案例,带你彻底打通任督二脉!
📊 一、生命周期全景对比图(建议收藏!)
flowchart TD
A[Vue 实例创建] --> B{API 风格}
B -->|选项式 Options API| C1[Vue 2]
B -->|选项式 Options API| C2[Vue 3]
B -->|组合式 Composition API| D[Vue 3]
C1 --> E1[beforeCreate<br/>created<br/>beforeMount<br/>mounted<br/>beforeUpdate<br/>updated<br/>beforeDestroy⚠️<br/>destroyed⚠️]
C2 --> E2[beforeCreate<br/>created<br/>beforeMount<br/>mounted<br/>beforeUpdate<br/>updated<br/>beforeUnmount✅<br/>unmounted✅]
D --> E3[setup() 替代<br/>beforeCreate/created<br/>onBeforeMount()<br/>onMounted()<br/>onBeforeUpdate()<br/>onUpdated()<br/>onBeforeUnmount()<br/>onUnmounted()]
E1 -.->|迁移重点| F[⚠️ beforeDestroy/destroyed<br/>→ beforeUnmount/unmounted]
E2 --> G[术语统一:<br/>“卸载”替代“销毁”]
E3 --> H[逻辑聚合:<br/>相关代码集中管理]
🔑 核心变迁速览表
| 钩子阶段 | Vue 2 (Options) | Vue 3 (Options) | Vue 3 (Composition) | 关键变化 |
|---|---|---|---|---|
| 创建阶段 | beforeCreate``created | 保留(兼容) | setup() 内逻辑 | 二者被 setup 合并替代 |
| 挂载阶段 | beforeMount``mounted | 保留 | onBeforeMount``onMounted | 命名加 on 前缀 |
| 更新阶段 | beforeUpdate``updated | 保留 | onBeforeUpdate``onUpdated | 同上 |
| 销毁阶段 | beforeDestroy⚠️destroyed⚠️ | beforeUnmount✅**unmounted✅** | onBeforeUnmount``onUnmounted | 术语革新:卸载 ≠ 销毁 |
| 新增能力 | - | renderTracked``renderTriggered | onRenderTracked``onRenderTriggered | 调试响应式依赖 |
💡 为什么改名?
Vue 3 中组件是“卸载”(unmount)而非“销毁”(destroy)——实例仍可被缓存复用(如<KeepAlive>),术语更精准!
🔄 二、迁移实战:三步平滑过渡
✅ 场景1:选项式 API 迁移(最小改动)
// Vue 2
export default {
beforeDestroy() {
window.removeEventListener('resize', this.handleResize)
},
destroyed() {
console.log('组件销毁')
}
}
// Vue 3 (Options API) —— 仅改名!
export default {
beforeUnmount() { // ✅ 替换 beforeDestroy
window.removeEventListener('resize', this.handleResize)
},
unmounted() { // ✅ 替换 destroyed
console.log('组件卸载')
}
}
📌 提示:Vue 3 仍兼容
beforeDestroy/destroyed,但控制台会警告,强烈建议更新。
✅ 场景2:Composition API 重构(逻辑聚合典范)
<!-- Vue 2 Options API(逻辑分散) -->
<script>
export default {
data() { return { timer: null } },
mounted() {
this.timer = setInterval(() => this.fetchData(), 5000)
},
beforeDestroy() {
clearInterval(this.timer)
},
methods: {
fetchData() { /* ... */ }
}
}
</script>
<!-- Vue 3 Composition API(逻辑内聚) -->
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { useFetch } from '@/composables' // 自定义组合函数
const { data, error, refresh } = useFetch('/api/status')
const timer = ref(null)
// 聚合:数据 + 定时器 + 清理
onMounted(() => {
timer.value = setInterval(refresh, 5000)
console.log('组件已挂载,开始轮询')
})
onUnmounted(() => {
clearInterval(timer.value)
console.log('组件卸载,清理定时器')
})
</script>
✨ 优势:相关逻辑(数据获取+定时器+清理)集中管理,可读性与可维护性飙升!
🛠️ 三、高频实战案例(附避坑指南)
💡 案例1:防内存泄漏——事件监听器清理
// Composition API 正确写法
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
const handler = () => console.log('resize')
window.addEventListener('resize', handler)
// ✅ 关键:保存移除函数引用
onUnmounted(() => {
window.removeEventListener('resize', handler)
})
})
❌ 错误写法:在 onUnmounted 中直接写新函数 → 无法移除监听器!
💡 案例2:自定义组合函数封装(复用利器)
// composables/useScroll.js
import { ref, onMounted, onUnmounted } from 'vue'
export function useScroll() {
const scrollTop = ref(0)
const onScroll = () => {
scrollTop.value = window.scrollY
}
onMounted(() => {
window.addEventListener('scroll', onScroll, { passive: true })
})
onUnmounted(() => {
window.removeEventListener('scroll', onScroll)
})
return { scrollTop }
}
// 组件中直接使用
<script setup>
import { useScroll } from '@/composables'
const { scrollTop } = useScroll()
</script>
✅ 价值:滚动逻辑封装成“能力模块”,任意组件复用,彻底告别重复代码!
💡 案例3:调试响应式依赖(Vue 3 独占)
import { onRenderTracked, onRenderTriggered } from 'vue'
// 跟踪哪些响应式属性触发了渲染
onRenderTracked((event) => {
console.log('依赖追踪:', event)
// { target: ..., key: 'count', type: 'get' }
})
// 跟踪哪个属性变化触发了重新渲染
onRenderTriggered((event) => {
console.log('触发更新:', event)
})
🔍 适用场景:排查“为什么组件反复渲染?”的性能问题神器!
⚠️ 四、避坑指南:血泪经验总结
| 陷阱 | 正确姿势 | 原理 |
|---|---|---|
在 updated 中修改状态 | 用 nextTick 或计算属性 | 避免无限循环更新 |
setup 中访问 this | 用 getCurrentInstance()(慎用) | setup 无 this 上下文 |
| 忘记清理副作用 | 所有 onMounted 配对 onUnmounted | 防止内存泄漏(定时器/监听器) |
onUpdated 中操作 DOM | 用 nextTick 包裹 | 确保 DOM 已更新 |
混淆 onBeforeUpdate 时机 | 用于“更新前”读取旧 DOM 状态 | 如:记录滚动位置再更新 |
🌟 五、最佳实践:写出优雅的生命周期代码
- 逻辑聚合原则
→ 将“数据获取+清理”封装到自定义组合函数(如useFetch) - 副作用显式声明
→ 所有外部操作(API/事件/DOM)必须配对清理逻辑 - 避免过度使用钩子
→ 优先用watch/computed替代updated中的逻辑 - 命名清晰化
// ❌ 模糊 onMounted(() => { init() }) // ✅ 明确 onMounted(initChatWidget) onUnmounted(cleanupChatWidget)
📌 结语:生命周期是“术”,架构思维是“道”
Vue 3 的生命周期变革,本质是 从“配置碎片”到“逻辑聚合”的范式升级:
- Options API:适合简单组件,迁移成本低
- Composition API:复杂业务首选,逻辑复用能力碾压
🌱 记住这句话:
“不要问‘该用哪个钩子’,而要问‘这段逻辑属于什么职责’。”
当你开始用useXxx封装能力,用onUnmounted守护资源——
你已真正拥抱 Vue 3 的工程化哲学。
📎 附录:速查备忘单(打印贴屏!)
| 场景 | Vue 2 | Vue 3 Options | Vue 3 Composition |
|---|---|---|---|
| 初始化数据 | created | created | setup() 内直接写 |
| 操作 DOM | mounted | mounted | onMounted(() => { ... }) |
| 清理资源 | beforeDestroy | beforeUnmount | onBeforeUnmount(() => { ... }) |
| 监听更新 | updated | updated | onUpdated(() => { ... }) |
| 调试渲染 | - | - | onRenderTracked / onRenderTriggered |
🔗 延伸学习
掌握生命周期,不是记住钩子名字,而是理解组件生命的每一次呼吸。
现在,打开你的编辑器,用 onMounted 写下今日第一行优雅代码吧! 💚
