Vue.js 生命周期全面解析
什么是Vue生命周期
Vue生命周期指的是Vue实例从创建到销毁的整个过程中所经历的一系列阶段。在每个阶段,Vue都会提供特定的"钩子函数"(Lifecycle Hooks),允许开发者在不同阶段插入自定义代码,从而实现对Vue实例的精细控制。
理解Vue生命周期对于开发高效、可维护的Vue应用至关重要,它能帮助我们:
-
知道在何时执行特定操作(如数据获取、DOM操作等)
-
避免在不恰当的阶段执行操作导致的错误
-
优化应用性能
-
更好地调试和解决问题
Vue生命周期图示
在深入讲解各个生命周期钩子之前,先看一下官方提供的生命周期图示:
开始
│
├── 初始化阶段
│ ├── beforeCreate
│ └── created
│
├── 挂载阶段
│ ├── beforeMount
│ └── mounted
│
├── 更新阶段
│ ├── beforeUpdate
│ └── updated
│
├── 卸载阶段
│ ├── beforeUnmount (Vue 3) / beforeDestroy (Vue 2)
│ └── unmounted (Vue 3) / destroyed (Vue 2)
│
└── 激活阶段 (仅keep-alive组件)├── activated└── deactivated
生命周期钩子详解
1. 初始化阶段
beforeCreate
-
调用时机:实例初始化之后,数据观测(data observer)和event/watcher事件配置之前被调用
-
特点:
-
此时无法访问到data、computed、methods等选项
-
常用于插件开发中执行一些初始化任务
-
export default {beforeCreate() {console.log('beforeCreate', this.$data); // undefinedconsole.log('beforeCreate', this.methodName); // undefined}
}
created
-
调用时机:实例创建完成后被立即调用
-
特点:
-
可以访问data、computed、methods等
-
尚未挂载DOM,$el属性不可用
-
常用于异步数据请求、初始化非响应式属性等
-
export default {data() {return {message: 'Hello Vue!'}},created() {console.log('created', this.message); // 'Hello Vue!'console.log('created', this.$el); // undefinedthis.fetchData(); // 常见的数据获取操作}
}
2. 挂载阶段
beforeMount
-
调用时机:在挂载开始之前被调用,相关的render函数首次被调用
-
特点:
-
模板已编译,但尚未将生成的DOM替换到页面中
-
很少使用,但在服务端渲染时是唯一可用的钩子
-
export default {beforeMount() {console.log('beforeMount', this.$el); // 原始的挂载元素}
}
mounted
-
调用时机:实例被挂载后调用
-
特点:
-
可以访问到渲染后的DOM
-
常用于需要操作DOM的库初始化(如图表库)
-
注意:不保证所有子组件也都一起被挂载
-
export default {mounted() {console.log('mounted', this.$el); // 渲染后的DOM元素this.initChart(); // 初始化图表库}
}
3. 更新阶段
beforeUpdate
-
调用时机:数据变化时,虚拟DOM重新渲染和打补丁之前调用
-
特点:
-
可以在更新前访问现有的DOM状态
-
适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器
-
export default {beforeUpdate() {console.log('beforeUpdate', this.message);}
}
updated
-
调用时机:由于数据更改导致的虚拟DOM重新渲染和打补丁后调用
-
特点:
-
可以执行依赖于DOM的操作
-
注意:避免在此钩子中更改状态,可能导致无限更新循环
-
不保证所有子组件也都一起被重绘
-
export default {updated() {console.log('updated', this.message);// 可以执行DOM相关操作,但要小心无限循环}
}
4. 卸载阶段
beforeUnmount (Vue 3) / beforeDestroy (Vue 2)
-
调用时机:实例销毁之前调用
-
特点:
-
实例仍然完全可用
-
适合执行清理操作,如清除定时器、取消事件监听等
-
export default {beforeUnmount() { // Vue 3// 或 beforeDestroy() { // Vue 2clearInterval(this.timer);window.removeEventListener('resize', this.handleResize);}
}
unmounted (Vue 3) / destroyed (Vue 2)
-
调用时机:实例销毁后调用
-
特点:
-
所有指令都已解绑,所有事件监听器都已移除
-
所有子实例也都被销毁
-
export default {unmounted() { // Vue 3// 或 destroyed() { // Vue 2console.log('组件已销毁');}
}
5. 缓存组件相关钩子 (keep-alive)
activated
-
调用时机:被keep-alive缓存的组件激活时调用
-
特点:
-
适合执行需要在组件显示时刷新的操作
-
export default {activated() {this.fetchData(); // 重新获取数据}
}
deactivated
-
调用时机:被keep-alive缓存的组件失活时调用
-
特点:
-
适合保存组件状态或清除占用资源的操作
-
export default {deactivated() {this.saveState(); // 保存组件状态}
}
Vue 2与Vue 3生命周期对比
Vue 2 | Vue 3 | 说明 |
---|---|---|
beforeCreate | beforeCreate | 基本相同 |
created | created | 基本相同 |
beforeMount | beforeMount | 基本相同 |
mounted | mounted | 基本相同 |
beforeUpdate | beforeUpdate | 基本相同 |
updated | updated | 基本相同 |
beforeDestroy | beforeUnmount | 功能相同,名称更语义化 |
destroyed | unmounted | 功能相同,名称更语义化 |
- | renderTracked | Vue 3新增,调试用 |
- | renderTriggered | Vue 3新增,调试用 |
errorCaptured | errorCaptured | 错误捕获,Vue 3有变化 |
生命周期最佳实践
1、数据获取:
- 使用created或mounted获取初始数据
- 对于SSR应用,使用created
2、DOM操作:
- 必须在mounted之后进行
- 避免在updated中进行DOM操作,可能导致无限循环
3、清理工作:
- 在beforeUnmount/beforeDestroy中清除定时器、事件监听等
4、性能优化:
- 避免在频繁触发的钩子(如updated)中执行重操作
5、组件通信:
- 父组件的mounted不保证子组件已挂载,使用$nextTick确保
常见问题与解决方案
1、为什么在created中获取不到DOM元素?
- 因为此时模板还未编译和挂载,应在mounted中操作DOM
2、mounted不保证所有子组件都挂载怎么办?
- 使用this.$nextTick确保所有子组件已渲染
mounted() {this.$nextTick(() => {// 这里可以确保所有子组件都已挂载});
}
3、如何在组件销毁前保存状态?
- 在beforeUnmount/beforeDestroy中保存需要持久化的状态
4、为什么updated中修改数据可能导致无限循环?
- 因为数据修改会触发更新,更新又会调用updated,形成循环
总结
Vue生命周期是Vue框架的核心概念之一,理解并正确使用各个生命周期钩子能够帮助我们:
-
在正确的时机执行代码
-
避免常见错误和性能问题
-
构建更健壮、可维护的Vue应用
随着Vue 3的推出,生命周期钩子名称更加语义化,并增加了新的调试钩子,但核心概念保持不变。掌握这些知识将为你开发高质量的Vue应用奠定坚实基础。