Vue3 生命周期钩子

生命周期流程图

开始

├─ 初始化阶段

│ │

│ ├─ setup() 🔵 Composition API入口

│ │

│ └─ beforeCreate() 🟡 Options API(组合式API中不存在)

│ │

│ └─ created() 🟡 Options API(组合式API中不存在)

├─ 挂载阶段

│ │

│ ├─ onBeforeMount() ⚪ 挂载前

│ │

│ ├─ 编译模板

│ │

│ ├─ 创建虚拟DOM

│ │

│ ├─ 挂载到真实DOM

│ │

│ └─ onMounted() ✅ 挂载完成

├─ 更新阶段

│ │

│ ├─ onBeforeUpdate() 🔄 更新前

│ │

│ ├─ 重新渲染虚拟DOM

│ │

│ ├─ 打补丁(DOM更新)

│ │

│ └─ onUpdated() ✅ 更新完成

├─ 卸载阶段

│ │

│ ├─ onBeforeUnmount() ⚠️ 卸载前

│ │

│ ├─ 移除DOM元素

│ │

│ └─ onUnmounted() ❌ 卸载完成

└─ 缓存组件特殊阶段(keep-alive)

├─ onActivated() 🔄 激活时

└─ onDeactivated() 💤 停用时

一、生命周期钩子对照表

Options API Composition API 调用时机 常用场景
beforeCreate ❌ 无对应 实例初始化后,数据观测/事件配置前 极少使用
created ❌ 无对应 实例创建完成,数据观测/事件配置完成 Options API 中用于数据初始化
beforeMount onBeforeMount 挂载开始之前 服务端渲染中获取数据
mounted onMounted 挂载完成后 DOM操作、数据请求、定时器、事件监听
beforeUpdate onBeforeUpdate 数据更新,DOM 重新渲染前 获取更新前的DOM状态
updated onUpdated 数据更新,DOM 重新渲染后 DOM依赖操作、第三方库初始化
beforeDestroy (Vue 2) onBeforeUnmount 实例销毁前 清理定时器、取消订阅、解绑事件
destroyed (Vue 2) onUnmounted 实例销毁后 极少使用
activated onActivated keep-alive 组件激活时 恢复数据、重新请求
deactivated onDeactivated keep-alive 组件停用时 暂停定时器、保存状态

二、组合式API用法详解

2.1 基本使用

javascript 复制代码
<script setup>
import { 
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onActivated,
  onDeactivated,
  ref 
} from 'vue'

const count = ref(0)

// 1. 挂载阶段
onBeforeMount(() => {
  console.log('1. onBeforeMount - 挂载前')
  // 此时组件尚未挂载到DOM
  console.log('DOM元素:', document.querySelector('.app')) // null
})

onMounted(() => {
  console.log('2. onMounted - 挂载完成')
  // ✅ 最佳实践:
  // - DOM操作(获取元素、初始化图表等)
  // - 发起数据请求
  // - 设置定时器
  // - 添加事件监听
  
  const el = document.querySelector('.app')
  console.log('DOM元素:', el) // 真实DOM
  
  // 示例:初始化图表
  // initChart()
  
  // 示例:请求数据
  // fetchData()
  
  // 示例:开始定时器
  // timer = setInterval(() => {}, 1000)
  
  // 示例:监听窗口大小变化
  // window.addEventListener('resize', handleResize)
})

// 2. 更新阶段
onBeforeUpdate(() => {
  console.log('3. onBeforeUpdate - 更新前')
  console.log('count更新前的值:', count.value)
  
  // 可以获取更新前的DOM状态
  const el = document.querySelector('.count')
  console.log('更新前文本:', el?.textContent)
})

onUpdated(() => {
  console.log('4. onUpdated - 更新完成')
  console.log('count更新后的值:', count.value)
  
  // ⚠️ 注意事项:
  // 1. 避免在updated中修改响应式数据,可能导致无限循环
  // 2. 确保DOM更新已经完成
  
  // ✅ 适用场景:
  // - 第三方库需要基于DOM初始化
  // - 获取更新后的DOM尺寸
  
  // 示例:重新初始化第三方库
  // if (chartInstance) {
  //   chartInstance.dispose()
  //   initChart()
  // }
})

// 3. 卸载阶段
onBeforeUnmount(() => {
  console.log('5. onBeforeUnmount - 卸载前')
  
  // ✅ 必须清理!防止内存泄漏
  // - 清除定时器
  // - 取消事件监听
  // - 清理全局状态
  // - 取消网络请求
  
  // 示例:清理定时器
  // if (timer) clearInterval(timer)
  
  // 示例:移除事件监听
  // window.removeEventListener('resize', handleResize)
  
  // 示例:取消未完成的请求
  // if (currentRequest) currentRequest.abort()
})

onUnmounted(() => {
  console.log('6. onUnmounted - 卸载完成')
  // 此时组件实例已完全销毁
  // 可用于记录日志或清理全局资源
})

// 4. keep-alive 相关
onActivated(() => {
  console.log('激活组件')
  // 组件从缓存中激活时调用
  // 恢复数据、重新开始定时器等
  
  // 示例:恢复定时器
  // if (timerPaused) {
  //   startTimer()
  //   timerPaused = false
  // }
})

onDeactivated(() => {
  console.log('停用组件')
  // 组件被缓存时调用
  // 暂停定时器、保存状态等
  
  // 示例:暂停定时器
  // pauseTimer()
  // timerPaused = true
})
</script>

<template>
  <div class="app">
    <div class="count">Count: {{ count }}</div>
    <button @click="count++">增加</button>
  </div>
</template>

2.2 执行顺序示例

javascript 复制代码
<!-- ParentComponent.vue -->
<script setup>
import ChildComponent from './ChildComponent.vue'
import { onMounted, ref } from 'vue'

const showChild = ref(true)

onMounted(() => {
  console.log('父组件 mounted')
})
</script>

<template>
  <div>
    <button @click="showChild = !showChild">切换子组件</button>
    <ChildComponent v-if="showChild" />
  </div>
</template>

<!-- ChildComponent.vue -->
<script setup>
import { onMounted, onUnmounted } from 'vue'

onMounted(() => {
  console.log('子组件 mounted')
})

onUnmounted(() => {
  console.log('子组件 unmounted')
})
</script>

<!-- 执行顺序:
1. 父组件 setup
2. 子组件 setup
3. 子组件 onMounted
4. 父组件 onMounted
5. 切换时:子组件 onUnmounted
-->

三、Options API 生命周期

javascript 复制代码
<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  
  // 1. 初始化阶段(在 Composition API 中被 setup 替代)
  beforeCreate() {
    console.log('beforeCreate')
    // 此时:data未初始化,methods未定义
    // console.log(this.count) // undefined
  },
  
  created() {
    console.log('created')
    // ✅ 可以访问:data、methods、computed
    // ❌ 不能访问:DOM($el)
    // 用途:数据初始化、API请求(服务端渲染)
    
    console.log(this.count) // 0
    console.log(this.$el)   // undefined
  },
  
  // 2. 挂载阶段
  beforeMount() {
    console.log('beforeMount')
    // 模板编译完成,但尚未挂载到DOM
    console.log(this.$el) // undefined 或 编译前的模板
  },
  
  mounted() {
    console.log('mounted')
    // 与组合式API的onMounted用途相同
    this.$nextTick(() => {
      // 确保DOM已完全更新
    })
  },
  
  // 3. 更新阶段
  beforeUpdate() {
    console.log('beforeUpdate')
  },
  
  updated() {
    console.log('updated')
    // 小心:不要在这里修改数据,可能导致循环
  },
  
  // 4. 卸载阶段(Vue 2 命名)
  beforeDestroy() {  // Vue 3 中仍可用,但建议用 beforeUnmount
    console.log('beforeDestroy')
  },
  
  destroyed() {      // Vue 3 中仍可用,但建议用 unmounted
    console.log('destroyed')
  },
  
  // 5. keep-alive 相关
  activated() {
    console.log('activated')
  },
  
  deactivated() {
    console.log('deactivated')
  },
  
  // 6. 错误处理(Vue 2.5+)
  errorCaptured(err, instance, info) {
    console.error('错误捕获:', err)
    // 可以返回 false 阻止错误继续向上传播
    return false
  }
}
</script>

四、生命周期使用统计

根据实际项目经验,各生命周期使用频率:

  1. 高频使用 (80% 场景)

    • onMounted - DOM操作、数据请求、事件监听

    • onUnmounted - 清理工作

  2. 中频使用 (15% 场景)

    • onUpdated - 第三方库集成

    • onBeforeUnmount - 确保清理

  3. 低频使用 (5% 场景)

    • onBeforeMount - 服务端渲染特殊需求

    • onBeforeUpdate - 特定优化场景

    • onActivated/onDeactivated - 使用 keep-alive 时

相关推荐
TT哇7 小时前
【实习】数字营销系统 银行经理端(interact_bank)前端 Vue 移动端页面的 UI 重构与优化
java·前端·vue.js·ui
蓝帆傲亦7 小时前
Web前端跨浏览器兼容性完全指南:构建无缝用户体验的最佳实践
前端
晴殇i7 小时前
【前端缓存】localStorage 是同步还是异步的?为什么?
前端·面试
不一样的少年_7 小时前
Chrome 插件实战:如何实现“杀不死”的可靠数据上报?
前端·javascript·监控
深度涌现7 小时前
DNS详解——域名是如何解析的
前端
小码哥_常8 小时前
Android内存泄漏:成因剖析与高效排查实战指南
前端
卤代烃8 小时前
✨ 形势比人强,Chrome 大佬也去搞 Gemini 了
前端·agent·vibecoding
偶像佳沛8 小时前
JS 对象
前端·javascript
Jing_Rainbow8 小时前
【React-6/Lesson89(2025-12-27)】React Context 详解:跨层级组件通信的最佳实践📚
前端·react.js·前端框架