面试官 : “ 说一下 Vue 的 8 个生命周期钩子都做了什么 ? ”
一、Vue3 8 个核心生命周期钩子(按执行顺序)
| 阶段 | 选项式 API 名称 | 组合式 API 名称 | 执行时机 | 核心作用 & 实战场景 |
|---|---|---|---|---|
| 初始化阶段 | beforeCreate | 无(setup 替代) | 实例创建前,数据 / 方法未初始化,this 不可用 |
Vue2 中用于初始化非响应式数据;Vue3 中逻辑移到 setup 最顶部(无响应式操作) |
| 初始化阶段 | created | 无(setup 替代) | 实例创建完成,数据 / 方法已初始化,DOM 未生成 | 1. 发起异步请求(接口请求);2. 初始化非 DOM 相关逻辑(如数据格式化) |
| 挂载阶段 | beforeMount | onBeforeMount | 挂载开始前,模板编译完成,DOM 未挂载到页面($el 未生成) |
1. 预操作 DOM 结构(如计算 DOM 尺寸,需结合 nextTick);2. 初始化第三方库(挂载前准备) |
| 挂载阶段 | mounted | onMounted | DOM 挂载完成($el 已挂载),页面可见 |
1. 操作真实 DOM(如初始化 ECharts / 地图);2. 发起依赖 DOM 的异步请求;3. 监听 DOM 事件 |
| 更新阶段 | beforeUpdate | onBeforeUpdate | 数据更新后,DOM 重新渲染前 | 1. 获取更新前的 DOM 状态(如旧输入框值);2. 取消不必要的监听 / 定时器(避免重复执行) |
| 更新阶段 | onUpdated | onUpdated | DOM 重新渲染完成,页面已更新 | 1. 获取更新后的 DOM 状态;2. 重新计算 DOM 相关数据(如滚动位置重置) |
| 卸载阶段 | beforeUnmount | onBeforeUnmount | 组件卸载前(实例仍可用,DOM 未销毁) | 1. 清理副作用(清除定时器 / 事件监听);2. 销毁第三方库实例(如 ECharts 销毁) |
| 卸载阶段 | unmounted | onUnmounted | 组件卸载完成,DOM 销毁,实例失效 | 1. 最终清理(如取消接口请求);2. 释放内存(清空大型数组 / 对象引用) |
二、关键细节(Vue3 核心变化)
1. setup 替代 beforeCreate/created
Vue3 中 setup 执行时机 = beforeCreate + created,这两个钩子在组合式 API 中被废弃,所有初始化逻辑直接写在 setup 中:
以下是 Vue3 生命周期钩子的完整可运行代码示例,包含选项式 API 和组合式 API(<script setup> 推荐写法) 两种风格,附带详细注释和实战场景(如接口请求、DOM 操作、定时器清理等),可直接复制到 Vue3 项目中运行。
三、组合式 API 示例(<script setup> 推荐)
Vue3 生命周期演示
<template>
<div class="life-cycle-demo">
<h3>Vue3 生命周期演示(组合式 API)</h3>
<!-- 绑定响应式数据,触发更新阶段 -->
<p>当前计数:{{ count }}</p>
<button @click="count++">点击更新计数(触发更新钩子)</button>
<!-- 挂载 ECharts 示例 DOM -->
<div id="chart" style="width: 300px; height: 200px; margin: 20px 0;"></div>
</div>
</template>
<script setup>
import {
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
unmounted
} from 'vue';
// 模拟 ECharts(实际需安装:npm install echarts)
import * as echarts from 'echarts';
// 🫱🫱🫱 1. setup 本身替代 beforeCreate + created(初始化阶段)
console.log('===== setup 执行(等价于 beforeCreate + created)=====');
// 响应式数据初始化
const count = ref(0);
// 模拟接口请求(created 阶段核心场景)
const fetchData = async () => {
try {
console.log('发起异步接口请求(created 阶段)');
// 模拟接口延迟
const res = await new Promise(resolve => {
setTimeout(() => resolve({ data: '模拟接口返回数据' }), 1000);
});
console.log('接口请求完成:', res.data);
} catch (err) {
console.error('接口请求失败:', err);
}
};
// 执行接口请求(等价于 created 中调用)
fetchData();
// 🫱🫱🫱 2. 挂载阶段:beforeMount(DOM 未挂载)
onBeforeMount(() => {
console.log('===== onBeforeMount 执行 =====');
console.log('DOM 未挂载,#chart 元素:', document.getElementById('chart')); // null
// 若需提前操作 DOM,需结合 nextTick
});
// 🫱🫱🫱 3. 挂载阶段:mounted(DOM 已挂载,核心操作 DOM 场景)
let myChart = null;
onMounted(() => {
console.log('===== onMounted 执行 =====');
console.log('DOM 已挂载,#chart 元素:', document.getElementById('chart')); // 存在
// 初始化 ECharts(依赖 DOM 的第三方库)
myChart = echarts.init(document.getElementById('chart'));
myChart.setOption({
title: { text: '生命周期演示图表' },
xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] },
yAxis: { type: 'value' },
series: [{ data: [120, 200, 150], type: 'bar' }]
});
// 模拟定时器(需在卸载阶段清理)
const timer = setInterval(() => {
console.log('定时器运行中(count:', count.value, ')');
}, 1000);
// 把定时器存到全局,方便卸载时清理
window.lifeCycleTimer = timer;
});
// 🫱🫱🫱 4. 更新阶段:beforeUpdate(数据更新,DOM 未重新渲染)
onBeforeUpdate(() => {
console.log('===== onBeforeUpdate 执行 =====');
console.log('数据已更新(count:', count.value, '),DOM 未刷新');
// 可获取更新前的 DOM 状态(如旧的图表数据)
});
// 🫱🫱🫱 5. 更新阶段:updated(DOM 已重新渲染)
onUpdated(() => {
console.log('===== onUpdated 执行 =====');
console.log('DOM 已更新(count:', count.value, ')');
// 若数据更新后需重新渲染图表
if (myChart) {
myChart.setOption({
series: [{ data: [120 + count.value * 10, 200 + count.value * 10, 150 + count.value * 10] }]
});
}
});
// 🫱🫱🫱 6. 卸载阶段:beforeUnmount(组件即将卸载,清理副作用)
onBeforeUnmount(() => {
console.log('===== onBeforeUnmount 执行 =====');
// 清理定时器
clearInterval(window.lifeCycleTimer);
// 销毁 ECharts 实例
if (myChart) {
myChart.dispose();
myChart = null;
}
console.log('副作用已清理(定时器、ECharts 已销毁)');
});
// 🫱🫱🫱 7. 卸载阶段:unmounted(组件已完全卸载)
unmounted(() => {
console.log('===== unmounted 执行 =====');
console.log('组件已卸载,DOM 已销毁,实例失效');
});
</script>
四、选项式 API 示例(兼容 Vue2 写法)
<template>
<div class="life-cycle-demo">
<h3>Vue3 生命周期演示(选项式 API)</h3>
<p>当前计数:{{ count }}</p>
<button @click="count++">点击更新计数</button>
<div id="chart" style="width: 300px; height: 200px; margin: 20px 0;"></div>
</div>
</template>
<script>
import * as echarts from 'echarts';
export default {
// 响应式数据
data() {
return {
count: 0,
myChart: null,
timer: null
};
},
// 🫱🫱🫱 1. 初始化阶段:beforeCreate(实例刚创建,数据/方法未初始化)
beforeCreate() {
console.log('===== beforeCreate 执行 =====');
console.log('数据未初始化:', this.count); // undefined
console.log('方法未初始化:', this.fetchData); // undefined
},
// 🫱🫱🫱 2. 初始化阶段:created(数据/方法已初始化,DOM 未生成)
created() {
console.log('===== created 执行 =====');
console.log('数据已初始化:', this.count); // 0
// 发起异步请求
this.fetchData();
},
// 🫱🫱🫱 3. 挂载阶段:beforeMount(模板编译完成,DOM 未挂载)
beforeMount() {
console.log('===== beforeMount 执行 =====');
console.log('DOM 未挂载,#chart 元素:', document.getElementById('chart')); // null
},
// 🫱🫱🫱 4. 挂载阶段:mounted(DOM 已挂载,可操作真实 DOM)
mounted() {
console.log('===== mounted 执行 =====');
console.log('DOM 已挂载,#chart 元素:', document.getElementById('chart')); // 存在
// 初始化 ECharts
this.myChart = echarts.init(document.getElementById('chart'));
this.myChart.setOption({
title: { text: '选项式 API 图表' },
xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] },
yAxis: { type: 'value' },
series: [{ data: [120, 200, 150], type: 'bar' }]
});
// 启动定时器
this.timer = setInterval(() => {
console.log('定时器运行中(count:', this.count, ')');
}, 1000);
},
// 🫱🫱🫱 5. 更新阶段:beforeUpdate(数据更新,DOM 未重新渲染)
beforeUpdate() {
console.log('===== beforeUpdate 执行 =====');
console.log('数据已更新(count:', this.count, '),DOM 未刷新');
},
// 🫱🫱🫱 6. 更新阶段:updated(DOM 已重新渲染)
updated() {
console.log('===== updated 执行 =====');
console.log('DOM 已更新(count:', this.count, ')');
// 重新渲染图表
if (this.myChart) {
this.myChart.setOption({
series: [{ data: [120 + this.count * 10, 200 + this.count * 10, 150 + this.count * 10] }]
});
}
},
// 🫱🫱🫱 7. 卸载阶段:beforeUnmount(组件即将卸载,清理副作用)
beforeUnmount() {
console.log('===== beforeUnmount 执行 =====');
// 清理定时器
clearInterval(this.timer);
// 销毁 ECharts
if (this.myChart) {
this.myChart.dispose();
this.myChart = null;
}
},
// 🫱🫱🫱 8. 卸载阶段:unmounted(组件已完全卸载)
unmounted() {
console.log('===== unmounted 执行 =====');
console.log('组件已卸载,资源已清理');
},
// 自定义方法:模拟接口请求
methods: {
async fetchData() {
try {
console.log('发起接口请求(created 阶段)');
const res = await new Promise(resolve => {
setTimeout(() => resolve({ data: '选项式 API 接口数据' }), 1000);
});
console.log('接口请求完成:', res.data);
} catch (err) {
console.error('接口请求失败:', err);
}
}
}
};
</script>
五、测试方式(验证生命周期执行)
-
挂载阶段:页面加载后,控制台会依次打印
setup/beforeCreate→created→beforeMount→mounted,同时 ECharts 图表渲染完成,定时器开始运行。 -
更新阶段:点击 “点击更新计数” 按钮,触发
beforeUpdate→updated,图表数据随计数更新。 -
卸载阶段:
- 若使用路由,跳转到其他页面(组件卸载);
- 或手动移除组件(如用
v-if控制),控制台会打印beforeUnmount→unmounted,定时器停止,ECharts 实例销毁。
六、核心注意点
-
组合式 API 无
beforeCreate/created:所有初始化逻辑直接写在<script setup>顶部,等价于这两个钩子。 -
副作用必须清理:定时器、事件监听、第三方库实例(如 ECharts)需在
onBeforeUnmount/beforeUnmount中清理,避免内存泄漏。 -
DOM 操作仅在
mounted/updated中安全:beforeMount中操作 DOM 需结合nextTick。 -
updated 中避免无限循环:不要在
updated中直接修改响应式数据(除非加条件判断)。
通过这个示例,你可以直观看到每个生命周期钩子的执行时机和实际用途,覆盖日常开发中 90% 以上的生命周期场景