Appearance
Vue3 生命周期完全指南
一、生命周期概述
Vue3 的生命周期是指 Vue 实例从创建到销毁的整个过程,在这个过程中会自动触发一系列的钩子函数,开发者可以在这些钩子函数中执行特定的业务逻辑。
二、Vue3 生命周期钩子
2.1 创建阶段
beforeCreate
执行时机:实例初始化之后,数据观测 (data observer) 和事件配置之前调用。
作用功能:
- 组件实例刚被创建,组件属性计算之前
- 此时
data、methods、computed等都还未初始化 - 无法访问
this.data、this.methods
使用场景:
- 极少使用,一般用于插件开发中的初始化工作
- 可以添加一些自定义的初始化逻辑
代码示例:
javascript
import { onBeforeMount } from 'vue'
setup() {
onBeforeMount(() => {
console.log('组件即将挂载')
})
}created
执行时机:实例创建完成后调用,此时实例已完成数据观测、属性和方法的运算,但 DOM 还未挂载。
作用功能:
data、methods、computed、watch都已初始化完成- 可以访问组件的数据和方法
- 无法访问 DOM 元素(
$el尚不存在)
使用场景:
- 调用异步请求获取初始数据
- 初始化组件状态
- 订阅事件或设置定时器
- 访问和操作组件数据
代码示例:
javascript
export default {
data() {
return {
userList: []
}
},
created() {
this.fetchUserList()
},
methods: {
async fetchUserList() {
this.userList = await api.getUsers()
}
}
}2.2 挂载阶段
beforeMount
执行时机:在挂载开始之前被调用,相关的 render 函数首次被调用。
作用功能:
- 模板编译完成,但还未渲染到页面
this.$el已存在但尚未挂载到 DOM- 此时 DOM 还是虚拟 DOM
使用场景:
- 在渲染前最后一次修改数据的机会
- 不会触发重新渲染的过程
代码示例:
javascript
export default {
data() {
return {
message: 'Hello'
}
},
beforeMount() {
this.message = 'Hello Vue3'
console.log('DOM 即将更新')
}
}mounted
执行时机:实例挂载到 DOM 后调用,此时可以访问 DOM 元素。
作用功能:
- 组件 DOM 已渲染完成
- 可以访问
this.$el - 子组件可能还未完成挂载(使用
this.$nextTick确保所有子组件挂载完成)
使用场景:
- 操作 DOM 元素
- 初始化第三方库(如 ECharts、Swiper 等)
- 绑定事件监听器
- 执行需要 DOM 存在的操作
代码示例:
javascript
export default {
mounted() {
this.initChart()
this.$nextTick(() => {
console.log('所有子组件已挂载完成')
})
},
methods: {
initChart() {
const chart = echarts.init(this.$refs.chartDom)
chart.setOption(this.chartOptions)
}
}
}2.3 更新阶段
beforeUpdate
执行时机:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
作用功能:
- 数据已更新,但 DOM 还未更新
- 可以在此进一步更改状态,不会触发附加的重渲染过程
使用场景:
- 获取更新前 DOM 的状态
- 在更新前保存某些数据(如滚动位置)
代码示例:
javascript
export default {
data() {
return {
items: []
}
},
beforeUpdate() {
this.savedScrollTop = window.scrollY
}
}updated
执行时机:由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。
作用功能:
- 组件 DOM 已更新完成
- 可以执行依赖于 DOM 更新后的操作
- 避免在此期间更改状态,可能导致无限循环
使用场景:
- DOM 更新后执行操作
- 重新计算布局或尺寸
- 注意:避免在此钩子中更改响应式数据
代码示例:
javascript
export default {
updated() {
this.$nextTick(() => {
const height = this.$refs.container.offsetHeight
console.log('容器高度:', height)
})
}
}2.4 销毁阶段
beforeUnmount (Vue3 新增)
执行时机:在卸载组件实例之前调用。
作用功能:
- 组件实例仍然完全可用
- 可以访问
this、data、methods等 - DOM 元素仍然存在
使用场景:
- 清除定时器
- 取消事件监听
- 取消网络请求
- 清除第三方实例
代码示例:
javascript
export default {
data() {
return {
timer: null
}
},
mounted() {
this.timer = setInterval(() => {
console.log('定时执行')
}, 1000)
},
beforeUnmount() {
clearInterval(this.timer)
window.removeEventListener('resize', this.handleResize)
}
}unmounted (Vue3 新增)
执行时机:卸载组件实例后调用。
作用功能:
- 组件的所有子组件都已卸载
- 所有的响应式效果都已停止
- DOM 元素已被移除
使用场景:
- 最终清理工作
- 释放内存
- 记录日志
代码示例:
javascript
export default {
unmounted() {
console.log('组件已完全卸载')
}
}2.5 特殊生命周期钩子
activated
执行时机:被 keep-alive 缓存的组件激活时调用。
作用功能:
- 组件从缓存中被激活时触发
- 可以获取到组件的最新状态
使用场景:
- 刷新缓存数据
- 重新发起网络请求
- 恢复滚动位置
- 重新启动定时器
代码示例:
javascript
export default {
activated() {
this.fetchLatestData()
this.restoreScrollPosition()
}
}deactivated
执行时机:被 keep-alive 缓存的组件停用时调用。
作用功能:
- 组件被缓存时触发
- 组件实例会被保留在内存中
使用场景:
- 暂停不必要的操作(如定时器)
- 保存组件状态
- 清除临时数据
代码示例:
javascript
export default {
deactivated() {
this.pauseVideo()
this.saveCurrentState()
}
}errorCaptured
执行时机:当捕获一个来自子孙组件的错误时被调用。
作用功能:
- 捕获子组件传递上来的错误
- 可以阻止错误继续向上传播
参数:
err:错误对象vm:发生错误的组件实例info:Vue 特定的错误信息
使用场景:
- 全局错误处理
- 错误日志上报
- 错误边界处理
代码示例:
javascript
export default {
errorCaptured(err, vm, info) {
console.error('捕获到错误:', err)
console.error('错误来源:', vm)
console.error('错误信息:', info)
// 上报错误到服务器
this.reportError(err, vm, info)
// 返回 false 阻止错误继续向上传播
return false
}
}renderTracked (Vue3 新增)
执行时机:跟踪虚拟 DOM 重新渲染时调用。
作用功能:
- 开发模式下调试使用
- 告知哪个操作触发了重新渲染
使用场景:
- 性能优化调试
- 追踪响应式依赖
代码示例:
javascript
export default {
renderTracked(e) {
console.log('渲染被追踪:', e)
}
}renderTriggered (Vue3 新增)
执行时机:虚拟 DOM 重新渲染被触发时调用。
作用功能:
- 开发模式下调试使用
- 告知是什么触发了重新渲染
使用场景:
- 性能优化调试
- 分析渲染触发原因
代码示例:
javascript
export default {
renderTriggered(e) {
console.log('渲染被触发:', e)
}
}serverPrefetch (SSR)
执行时机:服务器端渲染期间调用。
作用功能:
- 在服务器端渲染时预取数据
- 返回 Promise 以延迟渲染
使用场景:
- SSR 应用中预取异步数据
- 服务端数据初始化
代码示例:
javascript
export default {
data() {
return {
article: null
}
},
serverPrefetch() {
return fetchArticle(this.id).then(article => {
this.article = article
})
}
}三、Composition API 生命周期钩子
Vue3 的 Composition API 提供了与 Options API 对应的生命周期钩子函数:
3.1 钩子函数对照表
| Options API | Composition API |
|---|---|
| beforeCreate | setup() |
| created | setup() |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeUnmount | onBeforeUnmount |
| unmounted | onUnmounted |
| activated | onActivated |
| deactivated | onDeactivated |
| errorCaptured | onErrorCaptured |
| renderTracked | onRenderTracked |
| renderTriggered | onRenderTriggered |
3.2 Composition API 使用示例
javascript
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onActivated,
onDeactivated,
onErrorCaptured,
ref,
onScopeDispose
} from 'vue'
export default {
setup() {
const count = ref(0)
// beforeCreate 和 created 被 setup 替代
console.log('setup 执行,相当于 beforeCreate 和 created')
onBeforeMount(() => {
console.log('组件即将挂载')
})
onMounted(() => {
console.log('组件已挂载')
// 可以在这里操作 DOM
})
onBeforeUpdate(() => {
console.log('数据即将更新')
})
onUpdated(() => {
console.log('数据已更新')
})
onBeforeUnmount(() => {
console.log('组件即将卸载')
})
onUnmounted(() => {
console.log('组件已卸载')
})
onActivated(() => {
console.log('组件被激活')
})
onDeactivated(() => {
console.log('组件被停用')
})
onErrorCaptured((err, instance, info) => {
console.error('捕获错误:', err)
return false
})
// 新增:作用域销毁时调用
onScopeDispose(() => {
console.log('作用域销毁')
})
return {
count
}
}
}3.3 自定义组合式函数中的生命周期
javascript
import { onMounted, onUnmounted, ref } from 'vue'
// 自定义组合式函数
function useMousePosition() {
const x = ref(0)
const y = ref(0)
function update(e) {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => {
window.addEventListener('mousemove', update)
})
onUnmounted(() => {
window.removeEventListener('mousemove', update)
})
return { x, y }
}
// 在组件中使用
export default {
setup() {
const { x, y } = useMousePosition()
return { x, y }
}
}四、Vue3 与 Vue2 生命周期差异对比
4.1 钩子名称变更
| Vue2 | Vue3 | 说明 |
|---|---|---|
| beforeCreate | setup() | Vue3 中被 setup 替代 |
| created | setup() | Vue3 中被 setup 替代 |
| beforeMount | onBeforeMount | 名称不变,需在 setup 中使用 onXxx |
| mounted | onMounted | 名称不变 |
| beforeUpdate | onBeforeUpdate | 名称不变 |
| updated | onUpdated | 名称不变 |
| beforeDestroy | onBeforeUnmount | ⚠️ 重命名 |
| destroyed | onUnmounted | ⚠️ 重命名 |
| activated | onActivated | 名称不变 |
| deactivated | onDeactivated | 名称不变 |
| errorCaptured | onErrorCaptured | 名称不变 |
| - | onRenderTracked | ✨ Vue3 新增 |
| - | onRenderTriggered | ✨ Vue3 新增 |
4.2 主要差异详解
差异一:beforeCreate 和 created 被移除
Vue2 写法:
javascript
export default {
beforeCreate() {
console.log('实例初始化')
},
created() {
console.log('实例创建完成')
this.fetchData()
}
}Vue3 写法:
javascript
export default {
setup() {
// setup 本身就相当于 beforeCreate 和 created
console.log('setup 执行')
// 直接在 setup 中调用方法
fetchData()
return {}
}
}原因:Composition API 的 setup 在组件创建之前执行,因此不再需要这两个钩子。
差异二:销毁钩子重命名
Vue2:
javascript
export default {
beforeDestroy() {
console.log('即将销毁')
},
destroyed() {
console.log('已销毁')
}
}Vue3:
javascript
export default {
beforeUnmount() {
console.log('即将卸载')
},
unmounted() {
console.log('已卸载')
}
}
// 或 Composition API
import { onBeforeUnmount, onUnmounted } from 'vue'
setup() {
onBeforeUnmount(() => {
console.log('即将卸载')
})
onUnmounted(() => {
console.log('已卸载')
})
}原因:更准确地描述组件从 DOM 中卸载的过程,而不是"销毁"。
差异三:新增调试钩子
Vue3 新增:
javascript
import { onRenderTracked, onRenderTriggered } from 'vue'
export default {
setup() {
onRenderTracked(e => {
console.log('渲染被追踪:', e)
// e.key - 追踪的响应式属性
// e.target - 目标对象
// e.type - 追踪类型
})
onRenderTriggered(e => {
console.log('渲染被触发:', e)
// e.key - 触发的响应式属性
// e.target - 目标对象
// e.type - 触发类型
// e.newValue - 新值
// e.oldValue - 旧值
})
}
}用途:用于开发调试,帮助开发者了解组件渲染的触发原因。
4.3 完整对比示例
Vue2 Options API:
javascript
export default {
data() {
return {
message: 'Hello Vue2'
}
},
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
this.initData()
},
beforeMount() {
console.log('beforeMount')
},
mounted() {
console.log('mounted')
this.initChart()
},
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log('updated')
},
beforeDestroy() {
console.log('beforeDestroy')
this.cleanup()
},
destroyed() {
console.log('destroyed')
},
methods: {
initData() {},
initChart() {},
cleanup() {}
}
}Vue3 Composition API:
javascript
import {
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
export default {
setup() {
const message = ref('Hello Vue3')
// beforeCreate 和 created 被 setup 替代
console.log('setup (beforeCreate + created)')
initData()
onBeforeMount(() => {
console.log('onBeforeMount')
})
onMounted(() => {
console.log('onMounted')
initChart()
})
onBeforeUpdate(() => {
console.log('onBeforeUpdate')
})
onUpdated(() => {
console.log('onUpdated')
})
onBeforeUnmount(() => {
console.log('onBeforeUnmount')
cleanup()
})
onUnmounted(() => {
console.log('onUnmounted')
})
function initData() {}
function initChart() {}
function cleanup() {}
return {
message
}
}
}五、生命周期执行顺序
5.1 组件挂载流程
父组件 setup
↓
父组件 onBeforeMount
↓
子组件 setup
↓
子组件 onBeforeMount
↓
子组件 onMounted
↓
父组件 onMounted5.2 组件更新流程
父组件 onBeforeUpdate
↓
子组件 onBeforeUpdate
↓
子组件 onUpdated
↓
父组件 onUpdated5.3 组件卸载流程
父组件 onBeforeUnmount
↓
子组件 onBeforeUnmount
↓
子组件 onUnmounted
↓
父组件 onUnmounted5.4 完整执行顺序示例
javascript
// Parent.vue
import { onMounted, onBeforeMount } from 'vue'
export default {
setup() {
console.log('1. Parent setup')
onBeforeMount(() => {
console.log('2. Parent onBeforeMount')
})
onMounted(() => {
console.log('5. Parent onMounted')
})
}
}
// Child.vue
import { onMounted, onBeforeMount } from 'vue'
export default {
setup() {
console.log('3. Child setup')
onBeforeMount(() => {
console.log('4. Child onBeforeMount')
})
onMounted(() => {
console.log('6. Child onMounted')
})
}
}六、最佳实践
6.1 数据请求时机
javascript
// ✅ 推荐:在 created/setup 中请求
export default {
setup() {
const data = ref(null)
// setup 执行时立即请求,更早获取数据
fetchData().then(res => {
data.value = res
})
return { data }
}
}
// ✅ 需要 DOM 时在 mounted 中请求
export default {
setup() {
const chartRef = ref(null)
onMounted(async () => {
const data = await fetchData()
initChart(chartRef.value, data)
})
return { chartRef }
}
}6.2 清理资源
javascript
// ✅ 正确清理资源
export default {
setup() {
let timer = null
onMounted(() => {
timer = setInterval(() => {
console.log('定时任务')
}, 1000)
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
// 清理定时器
if (timer) {
clearInterval(timer)
timer = null
}
// 移除事件监听
window.removeEventListener('resize', handleResize)
})
function handleResize() {}
return {}
}
}6.3 使用组合式函数封装生命周期逻辑
javascript
// useEventListener.js
import { onUnmounted } from 'vue'
export function useEventListener(target, event, callback) {
onMounted(() => {
target.addEventListener(event, callback)
})
onUnmounted(() => {
target.removeEventListener(event, callback)
})
}
// 使用
import { useEventListener } from './useEventListener'
export default {
setup() {
useEventListener(window, 'resize', () => {
console.log('窗口大小改变')
})
}
}6.4 避免常见错误
javascript
// ❌ 错误:在 updated 中修改响应式数据
export default {
setup() {
const count = ref(0)
onUpdated(() => {
count.value++ // 会导致无限循环
})
return { count }
}
}
// ✅ 正确:使用 nextTick 或条件判断
export default {
setup() {
const count = ref(0)
const isUpdating = ref(false)
onUpdated(() => {
if (!isUpdating.value) {
isUpdating.value = true
nextTick(() => {
// 执行操作
isUpdating.value = false
})
}
})
return { count }
}
}七、生命周期流程图
┌─────────────────────────────────────────────────────────────┐
│ Vue3 生命周期流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ setup() │ ← 创建实例,替代 beforeCreate/created │
│ └──────┬──────┘ │
│ ↓ │
│ ┌─────────────────┐ │
│ │ onBeforeMount │ ← 挂载前,render 首次被调用 │
│ └────────┬────────┘ │
│ ↓ │
│ ┌─────────────────┐ │
│ │ onMounted │ ← 挂载完成,DOM 可访问 │
│ └────────┬────────┘ │
│ │ │
│ │ ┌─────────────────────────────────┐ │
│ │ │ 数据变化时 │ │
│ │ │ ┌─────────────────┐ │ │
│ │ │ │ onBeforeUpdate │ │ │
│ │ │ └────────┬────────┘ │ │
│ │ │ ↓ │ │
│ │ │ ┌─────────────────┐ │ │
│ │ │ │ onUpdated │ │ │
│ │ │ └────────┬────────┘ │ │
│ │ └───────────┼─────────────────────┘ │
│ │ │ │
│ ↓ ↓ │
│ ┌─────────────────┐ │
│ │onBeforeUnmount │ ← 卸载前,实例仍可用 │
│ └────────┬────────┘ │
│ ↓ │
│ ┌─────────────────┐ │
│ │ onUnmounted │ ← 卸载完成,清理工作 │
│ └─────────────────┘ │
│ │
│ 特殊钩子: │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ onActivated │ │ onDeactivated │ ← keep-alive │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │onRenderTracked │ │onRenderTriggered│ ← 调试用 │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ │
│ │onErrorCaptured │ ← 错误捕获 │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘八、总结
Vue3 生命周期核心变化
- Options API 兼容:Vue3 仍然支持 Options API 的生命周期钩子
- Composition API:提供
onXxx形式的钩子函数 - setup 替代:
beforeCreate和created被setup替代 - 命名优化:
beforeDestroy/destroyed重命名为beforeUnmount/unmounted - 新增调试钩子:
onRenderTracked和onRenderTriggered
选择建议
- 新项目:推荐使用 Composition API,代码组织更灵活
- 迁移项目:可以继续使用 Options API,逐步迁移
- 复杂组件:使用 Composition API 更容易复用和测试逻辑