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 时

相关推荐
掘金安东尼7 小时前
让 JavaScript 更容易「善后」的新能力
前端·javascript·面试
掘金安东尼7 小时前
用 HTMX 为 React Data Grid 加速实时更新
前端·javascript·面试
灵感__idea9 小时前
Hello 算法:众里寻她千“百度”
前端·javascript·算法
yinuo9 小时前
轻松接入大语言模型API -04
前端
袋鼠云数栈UED团队10 小时前
基于 Lexical 实现变量输入编辑器
前端·javascript·架构
cipher10 小时前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
UrbanJazzerati10 小时前
非常友好的Vue 3 生命周期详解
前端·面试
AAA阿giao10 小时前
从零构建一个现代登录页:深入解析 Tailwind CSS + Vite + Lucide React 的完整技术栈
前端·css·react.js
亦妤10 小时前
JS执行机制、作用域及作用域链
javascript
兆子龙11 小时前
像 React Hook 一样「自动触发」:用 Git Hook 拦住忘删的测试代码与其它翻车现场
前端·架构