Vue3 生命周期钩子详解

小飞兽 Vue.js 150 次阅读 2026-05-10

Vue3 生命周期钩子详解

概述

生命周期钩子让你在组件运行的不同阶段注入自定义逻辑。Vue3 的组合式 API 提供了一套与选项式 API 一一对应的生命周期钩子函数,每个钩子都在组件从创建到销毁的特定时机被调用。理解每个钩子的执行时机和适用场景,是编写健壮 Vue3 组件的必备技能。

组件生命周期全流程

Vue3 组件从创建到销毁经历以下阶段:初始化 → 挂载 → 更新 → 卸载。每个阶段都有对应的钩子函数,组合式 API 的钩子函数需在 setup<script setup> 中调用。

基础语法

import {
  onMounted,     // 挂载后:DOM 已插入,可访问 $el
  onUpdated,     // 更新后:VDOM 重新渲染并应用到真实 DOM
  onUnmounted,   // 卸载前:组件即将销毁,清理定时器、事件监听
  onBeforeMount, // 挂载前:render 函数首次被调用
  onBeforeUpdate,// 更新前:数据变化,DOM 更新前
  onBeforeUnmount,// 卸载前:组件实例仍完全正常
  onErrorCaptured,// 捕获子孙组件抛出的错误
  onRenderTracked,// VDOM render 时自动追踪响应式依赖(调试用)
  onRenderTriggered,// VDOM re-render 时手动追踪触发源(调试用)
  onActivated,   // 配合 keep-alive:被激活时
  onDeactivated, // 配合 keep-alive:被停用时
} from 'vue'

完整代码示例

<script setup>
import {
  ref, reactive, onMounted, onUpdated, onUnmounted,
  onBeforeMount, onBeforeUpdate, onBeforeUnmount,
  onErrorCaptured, onRenderTracked, onRenderTriggered,
  onActivated, onDeactivated, nextTick
} from 'vue'

console.log('[阶段0] setup 执行(组件初始化)')

// ========== 1. 挂载阶段 ==========
onBeforeMount(() => {
  // 真实 DOM 还不存在,不能访问 this.$el
  console.log('[阶段1] onBeforeMount - 即将挂载,el 是否存在:', false)
})

onMounted(() => {
  // 组件 DOM 已插入,可安全访问 this.$el、document.querySelector
  console.log('[阶段1] onMounted - DOM 已挂载')
  console.log(' mounted 可访问 el:', document.querySelector('.lifecycle-demo'))

  // 初始化第三方图表库(如 ECharts)
  initChart()

  // 绑定全局事件
  window.addEventListener('resize', handleResize)
})

// ========== 2. 更新阶段 ==========
const message = ref('初始消息')

function updateMessage() {
  message.value = '消息已更新 - ' + new Date().toLocaleTimeString()
}

onBeforeUpdate(() => {
  // DOM 还未更新,可在此做旧 DOM 快照
  const el = document.querySelector('.message-box')
  console.log('[阶段2] onBeforeUpdate - DOM 将更新,current text:', el?.textContent)
})

onUpdated(() => {
  // DOM 已更新完成,常用于操作更新后的 DOM(如聚焦光标)
  console.log('[阶段2] onUpdated - DOM 已更新')
  nextTick(() => {
    console.log('nextTick: DOM 完全稳定')
  })
})

// ========== 3. 卸载阶段 ==========
let timer = null

onBeforeUnmount(() => {
  // 组件仍完全可用,可取消异步请求
  console.log('[阶段3] onBeforeUnmount - 即将卸载,清理前准备')
  if (timer) clearInterval(timer)
})

onUnmounted(() => {
  // 完全清理:事件监听、定时器、订阅
  console.log('[阶段3] onUnmounted - 组件已卸载')
  window.removeEventListener('resize', handleResize)
  timer = null
})

// ========== 4. 错误捕获 ==========
onErrorCaptured((err, instance, info) => {
  console.error('[错误捕获] 捕获到错误:', err.message)
  console.error('[错误捕获] 错误来源组件:', instance)
  console.error('[错误捕获] 错误信息:', info)
  return false // 返回 false 阻止错误继续向上传播
})

// ========== 5. 调试追踪(开发模式) ==========
// 每次 render 追踪所有响应式依赖(类似松散模式的依赖日志)
onRenderTracked(({ key, target, type }) => {
  console.log(`[追踪] 访问了 ${type} ${String(key)} on`, target)
})

// 每次 render 由数据变化触发时,追踪是哪个数据变化导致的
onRenderTriggered(({ key, target, type }) => {
  console.log(`[触发] ${type} ${String(key)} 变化,触发了 re-render`)
})

// ========== 辅助函数 ==========
let chartInstance = null
function initChart() {
  // 模拟初始化 ECharts
  chartInstance = { render: () => console.log('图表渲染') }
  console.log('图表库初始化完成')
}

function handleResize() {
  if (chartInstance) chartInstance.render()
  console.log('窗口大小变化,重新布局')
}

// ========== keep-alive 钩子(配合 <keep-alive> 使用)==========
onActivated(() => {
  console.log('[keep-alive] 组件被激活(从缓存恢复)')
})

onDeactivated(() => {
  console.log('[keep-alive] 组件被停用(进入缓存)')
})
</script>

<template>
  <div class="lifecycle-demo">
    <h2>Vue3 生命周期演示</h2>

    <div class="message-box">{{ message }}</div>
    <button @click="updateMessage">更新消息</button>

    <p style="margin-top: 1rem; font-size: 0.85rem; color: #666;">
      打开浏览器控制台,观察各生命周期钩子的执行顺序。
    </p>

    <p style="margin-top: 0.5rem; font-size: 0.85rem; color: #666;">
      注意:在组件树中触发一个故意报错,查看 onErrorCaptured 的捕获效果。
    </p>
  </div>
</template>

<style scoped>
.lifecycle-demo { padding: 1rem; border: 1px solid #ddd; border-radius: 8px; }
</style>

运行效果说明

控制台按以下顺序输出日志:setup → onBeforeMount → onMounted(初始化)→ onBeforeUpdate → onUpdated(更新时)。点击"更新消息"按钮触发更新流程。组件卸载时 onBeforeUnmountonUnmounted 依次执行,定时器和事件监听被正确清理。如果子孙组件抛出错误,onErrorCaptured 捕获并打印详细信息。

常见问题

Q1:onMounted 和 onBeforeMount 的核心区别是什么?

onBeforeMount 在组件 render 函数执行之后、真实 DOM 插入之前调用,此时 document.querySelector 只能找到模板中使用了 ref/reactive 注册过的 DOM 占位符,无法安全操作真实 DOM。onMounted 在 DOM 真正插入完成后调用,此时可以安全执行 document.querySelector、初始化第三方库(如 ECharts、Swiper)、执行需要真实 DOM 尺寸的逻辑。

Q2:onUpdated 中如何避免无限更新循环?

onUpdated 中修改响应式数据会触发新一轮更新,容易形成死循环。解决方案:① 确保在 onUpdated 中只做纯 DOM 操作(如聚焦、滚动),不修改响应式数据;② 如必须修改,应使用 nextTick 包装,并通过条件判断防止重复执行;③ 检查数据修改是否有条件限制,避免每次更新都触发数据变化。

Q3:onErrorCaptured 和全局错误处理有什么区别?

onErrorCaptured 是组件级错误捕获,只能捕获当前组件及其子孙组件的错误,捕获后可以决定是否向上传播(返回 false 阻止传播)。app.config.errorHandler(Vue3 全局错误处理器)捕获所有未被处理的错误,包括 onErrorCaptured 返回 false 之后的错误。建议两者配合使用:局部用 onErrorCaptured 做组件特定的错误恢复,全局处理器负责统一日志上报。

延伸阅读

  • <a href="#">Vue3 组合式 API 完全指南</a> —— 组合式 API 完整入门
  • <a href="#">Vue3 依赖注入 provide/inject 详解</a> —— 深层组件间通信的响应式方案
  • <a href="#">Vue3 Teleport 组件深度解析</a> —— 任意位置渲染组件内容的技术实现