1. 概述
Vue 2 与 Vue 3 的生命周期在执行顺序和语义上基本一致,核心差异体现在:
| 维度 | Vue 2 | Vue 3 |
|---|---|---|
| API 风格 | 仅 Options API | Options API + Composition API |
| 命名规范 | beforeDestroy / destroyed |
beforeUnmount / unmounted |
| 初始化阶段 | beforeCreate → created |
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 |
|---|---|---|
| 执行时机 | beforeCreate → created |
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 中还能使用 beforeCreate 和 created 吗?
可以 ,在 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'