Vue 2 与 Vue 3 生命周期对比

1. 概述

Vue 2 与 Vue 3 的生命周期在执行顺序和语义上基本一致,核心差异体现在:

维度 Vue 2 Vue 3
API 风格 仅 Options API Options API + Composition API
命名规范 beforeDestroy / destroyed beforeUnmount / unmounted
初始化阶段 beforeCreatecreated setup() 统一替代
调试钩子 renderTracked / renderTriggered
SSR 支持 有限 onServerPrefetch

2. 生命周期钩子映射表

2.1 完整对照表

阶段 Vue 2 (Options API) Vue 3 (Options API) Vue 3 (Composition API) 说明
创建 beforeCreate beforeCreate setup() setup()beforeCreate 之前执行
创建 created created setup() 初始化逻辑统一放在 setup()
挂载 beforeMount beforeMount onBeforeMount DOM 挂载前
挂载 mounted mounted onMounted DOM 挂载完成,可安全访问 DOM
更新 beforeUpdate beforeUpdate onBeforeUpdate 数据变化,重新渲染前
更新 updated updated onUpdated 重新渲染完成
卸载 beforeDestroy beforeUnmount onBeforeUnmount Vue 3 更名为 unmount
卸载 destroyed unmounted onUnmounted 组件已卸载,清理工作在此进行
缓存 activated activated onActivated <KeepAlive> 组件激活时
缓存 deactivated deactivated onDeactivated <KeepAlive> 组件停用时
错误 errorCaptured errorCaptured onErrorCaptured 捕获后代组件错误
调试 --- renderTracked onRenderTracked 仅开发模式,追踪响应式依赖
调试 --- renderTriggered onRenderTriggered 仅开发模式,追踪渲染触发原因
SSR --- --- onServerPrefetch 服务端渲染时预取数据

2.2 命名变化速查

scss 复制代码
Vue 2              →   Vue 3
─────────────────────────────────────
beforeCreate       →   setup()(替代)
created            →   setup()(替代)
beforeMount        →   onBeforeMount
mounted            →   onMounted
beforeUpdate       →   onBeforeUpdate
updated            →   onUpdated
beforeDestroy      →   onBeforeUnmount  ⚠️ 更名
destroyed          →   onUnmounted      ⚠️ 更名
activated          →   onActivated
deactivated        →   onDeactivated
errorCaptured      →   onErrorCaptured

3. 代码对比示例

3.1 Vue 2 - Options API

javascript 复制代码
// Vue 2 组件
export default {
  name: 'UserProfile',

  data() {
    return {
      user: null,
      loading: false
    }
  },

  // 创建阶段
  beforeCreate() {
    console.log('[Vue2] beforeCreate: 实例初始化中')
  },

  created() {
    console.log('[Vue2] created: 实例已创建,可访问 data')
    this.fetchUserData()
  },

  // 挂载阶段
  beforeMount() {
    console.log('[Vue2] beforeMount: DOM 挂载前')
  },

  mounted() {
    console.log('[Vue2] mounted: DOM 已挂载,可访问 $el')
    this.initChart()
  },

  // 更新阶段
  beforeUpdate() {
    console.log('[Vue2] beforeUpdate: 数据变化,即将重新渲染')
  },

  updated() {
    console.log('[Vue2] updated: DOM 已更新')
  },

  // 卸载阶段(Vue 2 命名)
  beforeDestroy() {
    console.log('[Vue2] beforeDestroy: 实例销毁前,清理定时器等')
    clearInterval(this.timer)
  },

  destroyed() {
    console.log('[Vue2] destroyed: 实例已销毁')
  },

  methods: {
    async fetchUserData() {
      this.loading = true
      const res = await fetch('/api/user')
      this.user = await res.json()
      this.loading = false
    },
    initChart() {
      // 初始化图表库
    }
  }
}

3.2 Vue 3 - Options API(兼容写法)

javascript 复制代码
// Vue 3 兼容 Vue 2 写法,但卸载钩子已更名
export default {
  name: 'UserProfile',

  data() {
    return {
      user: null,
      loading: false
    }
  },

  // 创建阶段(与 Vue 2 一致)
  beforeCreate() {
    console.log('[Vue3 Options] beforeCreate')
  },

  created() {
    console.log('[Vue3 Options] created')
    this.fetchUserData()
  },

  // 挂载阶段(与 Vue 2 一致)
  beforeMount() {
    console.log('[Vue3 Options] beforeMount')
  },

  mounted() {
    console.log('[Vue3 Options] mounted')
    this.initChart()
  },

  // 更新阶段(与 Vue 2 一致)
  beforeUpdate() {
    console.log('[Vue3 Options] beforeUpdate')
  },

  updated() {
    console.log('[Vue3 Options] updated')
  },

  // ⚠️ 卸载阶段:Vue 3 已更名
  beforeUnmount() {
    console.log('[Vue3 Options] beforeUnmount: 替代 beforeDestroy')
    clearInterval(this.timer)
  },

  unmounted() {
    console.log('[Vue3 Options] unmounted: 替代 destroyed')
  },

  // 新增调试钩子
  renderTracked(event) {
    console.log('[Vue3 Debug] renderTracked:', event)
  },

  renderTriggered(event) {
    console.log('[Vue3 Debug] renderTriggered:', event)
  },

  methods: {
    async fetchUserData() {
      this.loading = true
      const res = await fetch('/api/user')
      this.user = await res.json()
      this.loading = false
    },
    initChart() {
      // 初始化图表库
    }
  }
}

3.3 Vue 3 - Composition API(推荐写法)

vue 复制代码
<script setup>
import { ref, onMounted, onBeforeUnmount, onUnmounted, onErrorCaptured } from 'vue'

// ==================== 状态定义 ====================
const user = ref(null)
const loading = ref(false)
let timer = null

// ==================== 创建阶段 ====================
// setup() 在 beforeCreate 之前执行
// 以下代码相当于 Vue 2 的 beforeCreate + created
console.log('[Vue3 Composition] setup() 执行:实例初始化阶段')

// 初始化逻辑直接写在这里
fetchUserData()

// ==================== 挂载阶段 ====================
onMounted(() => {
  console.log('[Vue3 Composition] onMounted: DOM 已挂载')
  initChart()

  // 启动定时器
  timer = setInterval(() => {
    console.log('定时任务执行中...')
  }, 5000)
})

// ==================== 卸载阶段 ====================
onBeforeUnmount(() => {
  console.log('[Vue3 Composition] onBeforeUnmount: 清理工作')
  clearInterval(timer)
})

onUnmounted(() => {
  console.log('[Vue3 Composition] onUnmounted: 组件已卸载')
})

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

// ==================== 方法定义 ====================
async function fetchUserData() {
  loading.value = true
  try {
    const res = await fetch('/api/user')
    user.value = await res.json()
  } catch (error) {
    console.error('获取用户数据失败:', error)
  } finally {
    loading.value = false
  }
}

function initChart() {
  // 初始化图表库
}
</script>

<template>
  <div>
    <div v-if="loading">加载中...</div>
    <div v-else-if="user">
      <h1>{{ user.name }}</h1>
      <p>{{ user.email }}</p>
    </div>
  </div>
</template>

3.4 Vue 3 - 多个同类型钩子

Composition API 支持同一个生命周期钩子注册多个回调,按注册顺序执行:

vue 复制代码
<script setup>
import { onMounted } from 'vue'

// 第一个 onMounted
onMounted(() => {
  console.log('第一个 mounted 回调:初始化图表')
})

// 第二个 onMounted
onMounted(() => {
  console.log('第二个 mounted 回调:绑定事件监听')
})

// 第三个 onMounted
onMounted(() => {
  console.log('第三个 mounted 回调:发送埋点数据')
})
</script>

4. 关键差异详解

4.1 setup() 替代 beforeCreate / created

特性 Vue 2 Vue 3 Composition API
执行时机 beforeCreatecreated setup() 在两者之前执行
this 访问 ✅ 有 this 没有 this
响应式数据 data() 返回对象 ref() / reactive()
方法定义 methods 对象 普通函数
javascript 复制代码
// Vue 2
export default {
  data() { return { count: 0 } },
  created() {
    this.count = 1  // 通过 this 访问
  }
}

// Vue 3 Composition API
import { ref } from 'vue'
const count = ref(0)
// setup() 顶部直接执行初始化
count.value = 1  // 直接访问,无需 this

4.2 卸载阶段更名原因

Vue 3 将 destroy 改为 unmount,语义更准确:

术语 含义
destroy 销毁(强调破坏、不可恢复)
unmount 卸载(强调从 DOM 中移除,可被 <KeepAlive> 缓存)
javascript 复制代码
// Vue 2
beforeDestroy() { /* 清理 */ }
destroyed() { /* 已销毁 */ }

// Vue 3
beforeUnmount() { /* 清理 */ }  // 更准确的语义
unmounted() { /* 已卸载 */ }

4.3 新增调试钩子

Vue 3 新增两个仅开发模式的调试钩子:

javascript 复制代码
import { onRenderTracked, onRenderTriggered } from 'vue'

// 追踪响应式依赖(首次渲染时触发)
onRenderTracked((event) => {
  console.log('追踪到依赖:', event.target, event.key)
})

// 追踪渲染触发原因(响应式数据变化时触发)
onRenderTriggered((event) => {
  console.log('渲染被触发:', event.target, event.key, event.type)
})

4.4 SSR 专用钩子

javascript 复制代码
import { onServerPrefetch } from 'vue'

// 仅在服务端渲染时执行
onServerPrefetch(async () => {
  const data = await fetchDataFromServer()
  // 数据会被序列化到 HTML 中,客户端直接复用
})

5. 执行顺序图示

5.1 完整生命周期流程

yaml 复制代码
┌─────────────────────────────────────────────────────────────┐
│                      组件初始化                               │
│  Vue 2: beforeCreate → created                               │
│  Vue 3: setup()  (统一替代 beforeCreate + created)          │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                      编译模板                                 │
│  解析 template → 生成 render 函数                             │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                      挂载阶段                                 │
│  Vue 2: beforeMount → mounted                                │
│  Vue 3: onBeforeMount → onMounted                            │
└─────────────────────────────────────────────────────────────┘
                            ↓
         ┌────────────────┴────────────────┐
         ↓                                 ↓
┌─────────────────────┐         ┌─────────────────────┐
│    数据变化触发更新   │         │    <KeepAlive> 缓存  │
│  beforeUpdate       │         │  activated          │
│  updated            │         │  deactivated        │
└─────────────────────┘         └─────────────────────┘
         ↓                                 ↓
┌─────────────────────────────────────────────────────────────┐
│                      卸载阶段                               │
│  Vue 2: beforeDestroy → destroyed                            │
│  Vue 3: onBeforeUnmount → onUnmounted                        │
└─────────────────────────────────────────────────────────────┘

5.2 父子组件执行顺序

scss 复制代码
父 beforeCreate
父 created
父 beforeMount
    子 beforeCreate
    子 created
    子 beforeMount
    子 mounted
父 mounted

// 更新时
父 beforeUpdate
    子 beforeUpdate
    子 updated
父 updated

// 卸载时
父 beforeUnmount (Vue3) / beforeDestroy (Vue2)
    子 beforeUnmount / beforeDestroy
    子 unmounted / destroyed
父 unmounted / destroyed

6. 迁移指南

6.1 从 Vue 2 迁移到 Vue 3

迁移项 Vue 2 代码 Vue 3 代码
初始化逻辑 created() { this.init() } setup() { init() }
DOM 操作 mounted() { this.$refs.el } onMounted(() => { elRef.value })
清理资源 beforeDestroy() { clearInterval() } onBeforeUnmount(() => { clearInterval() })
事件监听 mounted() { window.addEventListener(...) } onMounted + onBeforeUnmount 配对移除

6.2 渐进式迁移策略

xml 复制代码
阶段一:Vue 2.7 项目
├── 继续使用 Options API
├── 将 beforeDestroy → beforeUnmount
├── 将 destroyed → unmounted
└── 可逐步引入 Composition API

阶段二:Vue 3 新项目
├── 优先使用 <script setup> + Composition API
├── 复杂逻辑抽离为 composable 函数
└── 团队统一编码规范

6.3 常见迁移陷阱

javascript 复制代码
// ❌ 错误:在 setup() 中使用 this
setup() {
  console.log(this)  // undefined!
}

// ✅ 正确:直接使用导入的 API
import { getCurrentInstance } from 'vue'
// 或更推荐:通过参数传递、ref 获取

// ❌ 错误:在 setup() 顶部访问 DOM
setup() {
  const el = document.getElementById('app')  // DOM 还未生成!
}

// ✅ 正确:在 onMounted 中访问 DOM
onMounted(() => {
  const el = document.getElementById('app')  // 安全
})

7. 常见问题

Q1: Vue 3 中还能使用 beforeCreatecreated 吗?

可以 ,在 Options API 中仍然支持。但在 Composition API 的 setup() 中,这两个钩子不存在,其逻辑直接写在 setup() 函数顶部即可。

Q2: setup()created 的执行顺序?

setup()beforeCreate 之前 执行。如果同时使用 Options API 和 Composition API,setup() 最先执行。

Q3: 如何在 Composition API 中实现 created 的异步初始化?

javascript 复制代码
import { ref, onMounted } from 'vue'

const data = ref(null)
const isReady = ref(false)

// 同步初始化(相当于 created)
console.log('同步初始化')

// 异步初始化
async function init() {
  data.value = await fetchData()
  isReady.value = true
}

// 方案一:setup 顶部直接调用(注意:不会阻塞渲染)
init()

// 方案二:在 onMounted 中调用(推荐,确保 DOM 已就绪)
onMounted(() => {
  init()
})

Q4: Vue 3 的 onUnmounted 和 Vue 2 的 destroyed 完全等价吗?

不完全等价 。Vue 3 中组件可能被 <KeepAlive> 缓存,此时会触发 onDeactivated 而非 onUnmounted。只有当组件真正被移除时才会触发 onUnmounted

javascript 复制代码
// 组件被 <KeepAlive> 包裹时的生命周期
onActivated(() => {
  console.log('组件激活(从缓存恢复)')
})

onDeactivated(() => {
  console.log('组件停用(进入缓存)')
})

onUnmounted(() => {
  console.log('组件真正被销毁')
})

Q5: 为什么 Vue 3 推荐 Composition API?

优势 说明
逻辑复用 通过 composable 函数复用逻辑,替代 mixin
类型推断 更好的 TypeScript 支持
代码组织 按功能组织代码,而非按选项分散
树摇优化 未使用的 API 不会打包进产物

附录

A. 速查卡片

javascript 复制代码
Vue 3 Composition API 生命周期导入速查:

import {
  onBeforeMount,    // 挂载前
  onMounted,        // 挂载完成
  onBeforeUpdate,   // 更新前
  onUpdated,        // 更新完成
  onBeforeUnmount,  // 卸载前
  onUnmounted,      // 卸载完成
  onActivated,      // KeepAlive 激活
  onDeactivated,    // KeepAlive 停用
  onErrorCaptured,  // 错误捕获
  onRenderTracked,  // 调试:依赖追踪
  onRenderTriggered // 调试:渲染触发
} from 'vue'
相关推荐
Hilaku1 小时前
为什么你的大文件断点续传,一遇到弱网就崩溃?
前端·javascript·程序员
胡哈2 小时前
React Compiler:让开发者忘掉 memo 这件事
前端·react.js
xiyueyezibile2 小时前
“自迭代” Skill 的 team-pitfalls 的演进
前端·后端·ai编程
林恒smileZAZ2 小时前
`Array(100).map(() => 1)` 为什么全为空?
前端·javascript
小巧的砖头3 小时前
C#会重蹈覆辙吗?系列之2:反射及元数据的性能问题
开发语言·前端·c#
KaMeidebaby3 小时前
卡梅德生物技术快报|蛋白的叠氮基修饰:实操解析:核酸模板耦合蛋白的叠氮基修饰实现靶蛋白定点共价标记
前端·人工智能·物联网·算法·百度
濮水大叔3 小时前
在大型 Vue 项目中,如何有效管理复杂的状态?“散装版” vs “套装版”
前端·vue.js·typescript
玄星啊3 小时前
可修改:代码的生存底线
前端·ai编程
没落英雄4 小时前
3. DeepAgents 实战 - Memory Skills 与上下文工程
前端·人工智能·架构