一、Vue 生命周期概述
Vue 组件的生命周期指的是一个 Vue 实例从创建到销毁的整个过程。在这个过程中,Vue 提供了一系列的"钩子"(hooks),允许开发者在特定阶段插入自己的代码,实现特定的功能。
Vue 3 虽然引入了 Composition API,但依然保留了选项式 API 中的生命周期概念,只是部分钩子名称有所变化。
二、Vue 3 生命周期图示
初始化
│
├─ beforeCreate
│ ↓
├─ created
│ ↓
├─ beforeMount
│ ↓
├─ mounted
│ ↓
├─ beforeUpdate
│ ↓
├─ updated
│ ↓
├─ beforeUnmount (Vue 2中的beforeDestroy)
│ ↓
├─ unmounted (Vue 2中的destroyed)
│
└─ 其他特殊钩子├─ activated├─ deactivated└─ errorCaptured
三、主要生命周期钩子详解
1. beforeCreate
调用时机:在实例初始化之后,数据观测(data observer)和事件/侦听器配置之前被调用。
特点:
- 此时组件实例刚被创建
- data、methods、computed等选项还未初始化
- 无法访问组件的数据和方法
使用场景:
- 极少使用
- 可以在插件中使用来注入一些全局属性
2. created
调用时机:在实例创建完成后被立即调用。
特点:
- 已完成数据观测(data observer)
- 属性和方法的运算,watch/event事件回调已设置
- 但尚未挂载DOM,$el属性尚不可用
使用场景:
- 适合执行需要访问数据的初始化操作
- 可以在此处发起异步请求获取数据
- 可以访问响应式数据,但无法操作DOM
export default {data() {return {message: 'Hello Vue!'}},created() {console.log(this.message) // 'Hello Vue!'console.log(this.$el) // undefined}
}
3. beforeMount
调用时机:在挂载开始之前被调用,相关的render函数首次被调用。
特点:
- 编译模板已完成,但尚未将模板渲染到页面中
- 这是能在挂载前修改数据的最后机会
使用场景:
- 很少使用
- 可以在服务端渲染时使用
4. mounted
调用时机:实例被挂载后调用,这时el被新创建的vm.$el替换。
特点:
- 组件已挂载到DOM上
- 可以访问DOM元素
- 所有同步子组件也已被挂载(但不保证所有异步子组件都已挂载)
使用场景:
- 需要操作DOM的初始化
- 可以在此处使用第三方库初始化
- 可以获取元素尺寸或位置
export default {mounted() {console.log(this.$el) // 可以访问DOM元素this.$nextTick(() => {// 确保整个视图都已渲染})}
}
5. beforeUpdate
调用时机:数据更新时调用,发生在虚拟DOM打补丁之前。
特点:
- 可以在更新前访问现有的DOM
- 适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器
使用场景:
- 极少使用
- 可以在更新前获取DOM状态
6. updated
调用时机:由于数据更改导致的虚拟DOM重新渲染和打补丁后调用。
特点:
- 组件DOM已经更新
- 可以执行依赖于DOM的操作
- 避免在此钩子中更改状态,可能会导致无限更新循环
使用场景:
- 当需要根据DOM变化执行操作时
- 使用$nextTick确保所有子组件都已重新渲染
export default {updated() {this.$nextTick(() => {// 所有视图已更新后执行的代码})}
}
7. beforeUnmount (Vue 2中的beforeDestroy)
调用时机:在卸载组件实例之前调用。
特点:
- 实例仍然完全可用
- 这是销毁前的最后机会
使用场景:
- 清理定时器
- 取消事件监听
- 取消未完成的网络请求
export default {beforeUnmount() {clearInterval(this.timer)window.removeEventListener('resize', this.handleResize)}
}
8. unmounted (Vue 2中的destroyed)
调用时机:卸载组件实例后调用。
特点:
- 所有指令都已解绑
- 所有事件监听器都已移除
- 所有子实例也都被卸载
使用场景:
- 执行最后的清理工作
- 报告组件销毁
四、特殊生命周期钩子
1. activated
调用时机:被keep-alive缓存的组件激活时调用。
2. deactivated
调用时机:被keep-alive缓存的组件失活时调用。
使用场景:
- 保存/恢复组件状态
- 暂停/恢复动画
export default {activated() {// 恢复组件状态this.startAnimation()},deactivated() {// 保存组件状态this.pauseAnimation()}
}
3. errorCaptured
调用时机:捕获来自后代组件的错误时调用。
参数:
- err: 错误对象
- vm: 发生错误的组件实例
- info: 包含错误来源信息的字符串
特点:
- 可以返回false阻止错误继续向上传播
- 可以在此钩子中执行错误处理逻辑
export default {errorCaptured(err, vm, info) {console.error(`Error: ${err}\nInfo: ${info}`)// 发送错误报告sendErrorToServer(err)// 阻止错误继续向上传播return false}
}
五、Composition API中的生命周期钩子
Vue 3的Composition API提供了对应的生命周期函数,名称前加"on":
选项式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 |
使用示例:
import { onMounted, onUpdated, onUnmounted } from 'vue'export default {setup() {onMounted(() => {console.log('组件已挂载')})onUpdated(() => {console.log('组件已更新')})onUnmounted(() => {console.log('组件已卸载')})}
}
六、生命周期钩子的最佳实践
-
数据请求:通常在created或mounted中进行,如果数据不需要依赖DOM则在created中请求更早
-
DOM操作:必须在mounted或之后的生命周期中进行
-
清理工作:在beforeUnmount中清理定时器、事件监听器等资源
-
避免在updated中修改状态:可能导致无限循环更新
-
异步操作:使用$nextTick确保DOM更新完成
-
组合式API:在setup()中注册生命周期函数时,确保同步注册
七、常见问题
-
created和mounted的区别:
- created:数据已初始化,但DOM未生成
- mounted:DOM已生成,可操作DOM
-
为什么beforeCreate中无法访问data:
- 此时数据观测尚未初始化
-
异步组件生命周期:
- 异步组件的加载有额外的生命周期:
onBeforeRouteUpdate
等
- 异步组件的加载有额外的生命周期:
-
Vue 2到Vue 3的生命周期变化:
beforeDestroy
→beforeUnmount
destroyed
→unmounted