概述
Vue 生命周期是 Vue 实例从创建到销毁的整个过程,包含多个关键阶段,每个阶段都有对应的生命周期钩子函数,允许我们在特定时机执行自定义逻辑。
每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。
生命周期图示
创建
- 初始化组件的选项(data、methods、computed 等)
- 建立响应式数据系统
beforeCreate
- 时机:实例初始化之后,数据观测和事件配置之前
DOM正在构建/已完成,CSSOM可能尚未完成 - 特点:无法访问到
data、computed、methods等
可以访问this但值为空对象 - 常用场景:
created
- 时机:模板编译/挂载之前,初始化选项API之后
- 特点:可以访问
data、computed、methods
模板还未编译,$el属性还不存在 - 常用场景:异步请求、数据初始化
created() {
console.log('created', this.message); // 'Hello Vue'
console.log('created', this.$el); // undefined
// 适合在这里调用API获取初始数据
this.fetchData();
}
挂载
Vue的挂载阶段是组件从创建到渲染到真实DOM的过程,主要包括两个关键钩子函数
beforeMount
-
时机:在挂载开始之前被调用,此时模板编译已完成,但尚未将真实DOM插入页面。
-
特点:
- 虚拟DOM已经生成
- 模板已编译成render函数
- 尚未替换el内部的HTML内容
- 无法直接操作DOM元素
- 常用场景:
beforeMount() {
// 1. 最后一次数据修改机会(不会触发重渲染)
this.someData = this.processData(this.someData);
// 2. 初始化一些不依赖DOM的配置
this.initConfig();
// 3. 服务端渲染(SSR)中唯一可用的挂载阶段钩子
}
mounted
-
时机:实例挂载完成后调用,此时真实DOM已经渲染完成。
-
特点:
- 真实DOM已生成并插入页面
- 可访问和操作DOM元素
- 可访问子组件
- 不保证所有子组件都已挂载(需使用$nextTick)
- 常用场景:
mounted() {
// 1. DOM操作
this.$refs.input.focus();
// 2. 第三方DOM库初始化
new Chart(this.$refs.canvas, this.chartData);
// 3. 发起数据请求
this.fetchData();
// 4. 添加事件监听
window.addEventListener('resize', this.handleResize);
// 5. 确保子组件已挂载
this.$nextTick(() => {
// 所有子组件都已挂载
});
}
总结对比
| 特性 | beforeMount | mounted |
|---|---|---|
| 访问el | ❌ undefined | ✅ 可访问 |
| 访问真实DOM | ❌ 不可 | ✅ 可 |
| 服务端渲染 | ✅ 可用 | ❌ 不可用 |
| 数据修改 | 不触发更新 | 触发更新 |
| 主要用途 | 最后的数据处理 | DOM操作、请求、插件初始化 |
更新
更新阶段是当响应式数据发生变化时,Vue重新渲染组件的过程,主要包括两个关键钩子函数
beforeUpdate
-
时机:数据变化后,DOM重新渲染之前调用。
-
特点:
- 可以访问更新前的DOM状态
- 数据已经更新,但视图尚未同步
- 适合在更新前访问现有DOM
- 避免在此阶段修改数据(可能导致无限循环)
-
常用场景:
beforeUpdate() {
// 1. 获取更新前的DOM状态(如滚动位置)
this.scrollPosition = this.$refs.container.scrollTop;
// 2. 手动移除动态添加的内容
this.cleanupDynamicContent();
// 3. 记录变化前的状态用于对比
this.beforeData = { ...this.formData };
// 4. 手动处理DOM操作前的准备工作
this.$refs.message.innerHTML = '数据更新中...';
// 5. 计算需要保持的状态(如滚动位置保持)
this.shouldRestoreScroll = true;
}
updated
-
时机:数据变化导致DOM重新渲染完成后调用。
-
特点:
- DOM已更新,可以获取最新DOM状态
- 可以执行依赖于DOM的操作
- 避免在此修改数据(可能导致无限循环)
- 不保证所有子组件都已更新(需用$nextTick)
-
常用场景:
updated() {
// 1. 获取更新后的DOM信息(如元素高度、宽度)
const newHeight = this.$refs.content.offsetHeight;
// 2. 更新完成后滚动到底部或指定位置
if (this.autoScroll) {
this.$refs.chatContainer.scrollTop =
this.$refs.chatContainer.scrollHeight;
}
// 3. 使用$nextTick确保所有子组件更新完成
this.$nextTick(() => {
this.updateComplete = true;
});
// 4. 第三方图表库重新渲染
if (this.chart) {
this.chart.resize();
}
// 5. 触发自定义事件通知外部状态变化
this.$emit('updated', this.getLatestData());
}
总结对比
| 特性 | beforeUpdate | updated |
|---|---|---|
| 执行时机 | DOM更新前 | DOM更新后 |
| 数据状态 | 已更新 | 已更新 |
| DOM状态 | 旧DOM | 新DOM |
| 修改数据 | 谨慎使用 | 极不推荐 |
| 主要用途 | 获取更新前状态、准备操作 | DOM相关操作、第三方库更新 |
| 执行频率 | 每次数据变化 | 每次数据变化 |
卸载
卸载阶段是组件从DOM中移除、清理资源的过程,主要包括两个关键钩子函数
beforeUnmount (Vue 3) / beforeDestroy (Vue 2)
-
时机:组件卸载前调用,实例仍然完全可用。
-
特点:
- 组件实例仍完全可用
- 可以访问data、methods等
- 适合清理资源
- 组件还未销毁
-
常用场景:
// Vue 3 Composition API
onBeforeUnmount(() => {
// 1. 清除定时器
clearInterval(this.timer);
clearTimeout(this.timeout);
// 2. 取消网络请求
if (this.pendingRequest) {
this.pendingRequest.cancel();
}
// 3. 移除全局事件监听
window.removeEventListener('resize', this.handleResize);
document.removeEventListener('click', this.handleClick);
// 4. 销毁第三方库实例
if (this.chart) {
this.chart.dispose();
}
// 5. 取消订阅
this.$bus.off('event', this.handleEvent);
})
// Vue 2 Options API
beforeDestroy() {
// 1. 清除定时器
clearInterval(this.timer);
// 2. 取消网络请求
if (this.pendingRequest) {
this.pendingRequest.cancel();
}
// 3. 移除全局事件监听
window.removeEventListener('resize', this.handleResize);
}
unmounted (Vue 3) / destroyed (Vue 2)
-
时机:组件卸载后调用,此时组件实例已被销毁。
-
特点:
- 组件实例已被销毁
- 所有指令解绑
- 所有事件监听已移除
- 无法访问组件数据和方法
-
常用场景:
// Vue 3 Composition API
onUnmounted(() => {
// 1. 最终的清理确认
console.log('组件已卸载');
// 2. 触发外部通知
this.$emit('destroyed');
// 3. 记录日志
console.log('组件销毁完成', this.$options.name);
})
// Vue 2 Options API
destroyed() {
// 1. 最终的清理确认
console.log('组件已销毁');
// 2. 触发外部通知
this.$emit('destroyed');
// 3. 清理DOM引用
this.$refs = {};
}
总结对比
| 特性 | beforeUnmount/beforeDestroy | unmounted/destroyed |
|---|---|---|
| 执行时机 | 卸载前 | 卸载后 |
| 实例状态 | 完全可用 | 已销毁 |
| 访问data | ✅ 可访问 | ❌ 不可访问 |
| 访问methods | ✅ 可调用 | ❌ 不可调用 |
| 主要用途 | 清理资源、取消订阅 | 最终确认、日志记录 |
| 事件监听 | 可移除 | 已自动移除 |
特殊钩子函数
activated
-
时机:被keep-alive缓存的组件激活时调用。
-
特点:
- 组件从缓存中重新激活
- 适用于频繁切换的组件
- 可替代mounted的部分功能
-
常用场景:
activated() {
// 1. 刷新数据
this.refreshData();
// 2. 恢复状态
this.restoreState();
// 3. 重新添加事件监听
window.addEventListener('scroll', this.handleScroll);
}
deactivated
-
时机:被keep-alive缓存的组件停用时调用。
-
特点:
- 组件被缓存而非销毁
- 组件实例仍保留
- 适合暂停操作而非清理
-
常用场景:
deactivated() {
// 1. 暂停视频播放
this.pauseVideo();
// 2. 保存当前状态
this.saveState();
// 3. 移除临时事件监听
window.removeEventListener('scroll', this.handleScroll);
}
errorCaptured
-
时机:捕获后代组件错误时调用。
-
特点:
- 可捕获子组件、孙组件的错误
- 返回false可阻止错误继续传播
- 可用于错误处理和上报
-
常用场景:
errorCaptured(err, vm, info) {
// 1. 错误日志上报
this.logErrorToServer(err, info);
// 2. 显示错误提示
this.errorMessage = '组件加载失败';
// 3. 阻止错误继续传播
return false;
}
完整生命周期对比表
| 阶段 | Vue 2 | Vue 3 (Options) | Vue 3 (Composition) | 主要用途 |
|---|---|---|---|---|
| 创建 | beforeCreate | beforeCreate | setup() | 初始化前 |
| 创建 | created | created | setup() | 初始化完成 |
| 挂载 | beforeMount | beforeMount | onBeforeMount | 挂载前准备 |
| 挂载 | mounted | mounted | onMounted | DOM操作、请求 |
| 更新 | beforeUpdate | beforeUpdate | onBeforeUpdate | 更新前状态获取 |
| 更新 | updated | updated | onUpdated | 更新后DOM操作 |
| 卸载 | beforeDestroy | beforeUnmount | onBeforeUnmount | 清理资源 |
| 卸载 | destroyed | unmounted | onUnmounted | 销毁确认 |
| 缓存 | activated | activated | onActivated | 缓存激活 |
| 缓存 | deactivated | deactivated | onDeactivated | 缓存停用 |
| 错误 | errorCaptured | errorCaptured | onErrorCaptured | 错误处理 |