Vue框架之生命周期主要钩子函数详解
-
- 一、Vue生命周期的整体流程
- 二、创建阶段:初始化组件实例
-
- [2.1 `beforeCreate`:实例创建前](#2.1
beforeCreate
:实例创建前) - [2.2 `created`:实例创建后](#2.2
created
:实例创建后)
- [2.1 `beforeCreate`:实例创建前](#2.1
- 三、挂载阶段:组件与DOM结合
-
- [3.1 `beforeMount`:挂载前](#3.1
beforeMount
:挂载前) - [3.2 `mounted`:挂载后](#3.2
mounted
:挂载后)
- [3.1 `beforeMount`:挂载前](#3.1
- 四、更新阶段:数据变化触发重渲染
-
- [4.1 `beforeUpdate`:更新前](#4.1
beforeUpdate
:更新前) - [4.2 `updated`:更新后](#4.2
updated
:更新后)
- [4.1 `beforeUpdate`:更新前](#4.1
- 五、销毁阶段:组件实例的清理
-
- [5.1 `beforeDestroy`:销毁前](#5.1
beforeDestroy
:销毁前) - [5.2 `destroyed`:销毁后](#5.2
destroyed
:销毁后)
- [5.1 `beforeDestroy`:销毁前](#5.1
- 六、特殊场景的钩子函数
-
- [6.1 keep-alive 相关钩子](#6.1 keep-alive 相关钩子)
- [6.2 错误捕获钩子:`errorCaptured`](#6.2 错误捕获钩子:
errorCaptured
)
- 七、生命周期钩子的执行顺序与实战示例
-
- [7.1 单组件执行顺序](#7.1 单组件执行顺序)
- [7.2 父子组件执行顺序](#7.2 父子组件执行顺序)
- 八、常见问题与避坑指南
-
- [8.1 避免在`updated`中修改数据](#8.1 避免在
updated
中修改数据) - [8.2 子组件`mounted`晚于父组件`mounted`](#8.2 子组件
mounted
晚于父组件mounted
) - [8.3 清理资源是重中之重](#8.3 清理资源是重中之重)
- [8.1 避免在`updated`中修改数据](#8.1 避免在
- [九、Vue 3中的生命周期变化](#九、Vue 3中的生命周期变化)
Vue的生命周期是指组件从创建到销毁的整个过程,而生命周期钩子函数则是在这个过程中特定时间点自动执行的函数。掌握这些钩子函数,能让我们在合适的时机执行特定操作(如数据请求、DOM操作、资源清理等),是Vue开发的核心基础。
一、Vue生命周期的整体流程
Vue组件的生命周期可分为4个阶段,每个阶段包含若干钩子函数,整体流程如下:
- 创建阶段:组件实例从初始化到挂载前的过程
- 挂载阶段:组件实例挂载到DOM的过程
- 更新阶段:组件数据变化导致重新渲染的过程
- 销毁阶段:组件实例从DOM中移除并清理资源的过程
二、创建阶段:初始化组件实例
创建阶段是组件实例从无到有的过程,主要完成数据观测(响应式处理)、事件初始化等工作,此时尚未涉及DOM操作。
2.1 beforeCreate
:实例创建前
- 执行时机 :Vue实例初始化后(
new Vue()
之后),数据观测(data
、props
)和事件机制初始化前调用。 - 特点 :
- 无法访问
data
、props
、methods
中的数据和方法(此时尚未初始化)。 - 不能进行DOM操作(DOM尚未生成)。
- 无法访问
- 适用场景:极少使用,可用于初始化非响应式数据(如临时变量)。
javascript
new Vue({
data() {
return { message: 'Hello' };
},
beforeCreate() {
console.log('beforeCreate:', this.message); // undefined(无法访问data)
console.log('methods:', this.getMsg); // undefined(无法访问methods)
},
methods: {
getMsg() { return this.message; }
}
});
2.2 created
:实例创建后
- 执行时机 :Vue实例初始化完成后调用,此时已完成
data
、props
的响应式处理和methods
的绑定,但尚未开始DOM编译(模板未挂载到DOM)。 - 特点 :
- 可访问
data
、props
、methods
中的数据和方法。 - 仍无法进行DOM操作(
$el
属性不存在,DOM未生成)。
- 可访问
- 适用场景 :
- 发起初始化数据请求(如获取列表数据)。
- 初始化数据(如对
data
中的数据进行预处理)。 - 绑定自定义事件。
javascript
new Vue({
el: '#app',
data() {
return { list: [] };
},
created() {
console.log('created:', this.list); // [](可访问data)
console.log('$el:', this.$el); // undefined(DOM未挂载)
// 示例:发起数据请求
axios.get('/api/list')
.then(response => {
this.list = response.data; // 数据响应后更新list(响应式)
});
}
});
三、挂载阶段:组件与DOM结合
挂载阶段是组件实例与DOM关联的过程,核心是将编译后的模板挂载到页面中,此时开始具备DOM操作能力。
3.1 beforeMount
:挂载前
- 执行时机:模板编译(解析指令、插值表达式等)完成后,DOM挂载到页面之前调用。
- 特点 :
- 已完成模板编译,生成虚拟DOM(但未渲染到页面)。
$el
属性存在(指向即将挂载的DOM元素),但内容仍为原始模板(未替换数据)。- 仍无法操作DOM(数据未渲染,操作无意义)。
- 适用场景:极少使用,可用于获取模板编译前的DOM结构。
javascript
new Vue({
el: '#app',
template: '<div>{{ message }}</div>',
data() { return { message: '挂载前' }; },
beforeMount() {
console.log('beforeMount $el:', this.$el); // <div>{{ message }}</div>(未替换)
console.log('页面内容:', document.getElementById('app').innerHTML); // 原始模板
}
});
3.2 mounted
:挂载后
- 执行时机:模板挂载到DOM后调用,此时页面已显示渲染后的内容。
- 特点 :
- DOM已完全渲染,可通过
$el
或原生DOM API(如document.getElementById
)操作DOM。 - 子组件的
mounted
可能在父组件的mounted
之后执行(因渲染顺序)。
- DOM已完全渲染,可通过
- 适用场景 :
- 执行DOM操作(如初始化第三方UI插件:图表、地图等,需依赖DOM元素)。
- 监听DOM事件(如滚动、resize)。
- 若数据请求依赖DOM尺寸(如根据容器宽度请求不同数据),可在此发起请求。
javascript
new Vue({
el: '#app',
data() { return { width: 0 }; },
mounted() {
// 获取DOM元素宽度
this.width = this.$el.offsetWidth;
console.log('挂载后宽度:', this.width);
// 初始化第三方图表插件(假设页面有<div id="chart"></div>)
this.chart = new Chart(document.getElementById('chart'), {
type: 'line',
data: { labels: ['1月', '2月'], datasets: [{ data: [10, 20] }] }
});
// 监听滚动事件
window.addEventListener('scroll', this.handleScroll);
},
methods: {
handleScroll() { /* 处理滚动逻辑 */ }
}
});
四、更新阶段:数据变化触发重渲染
当组件的data
或props
发生变化时,会进入更新阶段,触发重新渲染,此阶段的钩子函数用于监控或干预更新过程。
4.1 beforeUpdate
:更新前
- 执行时机:数据发生变化后,虚拟DOM重新渲染前调用。
- 特点 :
- 此时
data
中的数据已更新,但DOM尚未重新渲染(页面显示旧数据)。 - 可获取更新前的DOM状态。
- 此时
- 适用场景:获取更新前的DOM信息(如滚动位置、输入框光标位置),用于更新后恢复状态。
javascript
new Vue({
el: '#app',
data() { return { count: 0 }; },
template: '<div>{{ count }} <button @click="count++">+1</button></div>',
beforeUpdate() {
console.log('更新前data:', this.count); // 新值(如1)
console.log('更新前DOM:', this.$el.textContent); // 旧值(如0)
}
});
4.2 updated
:更新后
- 执行时机:虚拟DOM重新渲染并更新到页面后调用,此时页面显示最新数据。
- 特点 :
data
和DOM均已更新,可获取最新的DOM状态。- 若在
updated
中修改data
,会再次触发更新(可能导致无限循环,需避免)。
- 适用场景 :
- 基于最新DOM状态执行操作(如根据新内容调整元素样式)。
- 同步第三方插件数据(如图表数据更新后,重新绘制图表)。
javascript
new Vue({
el: '#app',
data() { return { data: [10, 20] }; },
template: '<div>{{ data }}</div>',
updated() {
console.log('更新后DOM:', this.$el.textContent); // 显示最新data
// 若图表数据依赖this.data,更新后重新绘制
if (this.chart) {
this.chart.data.datasets[0].data = this.data;
this.chart.update();
}
}
});
五、销毁阶段:组件实例的清理
当组件被销毁(如v-if="false"
移除组件、路由切换)时,进入销毁阶段,此阶段的钩子函数用于清理资源,避免内存泄漏。
5.1 beforeDestroy
:销毁前
- 执行时机:组件实例销毁前调用,此时组件仍处于正常工作状态。
- 特点 :
- 可访问
data
、methods
、DOM等所有资源。 - 子组件的
beforeDestroy
会在父组件的beforeDestroy
前执行。
- 可访问
- 适用场景 :
- 清理定时器、事件监听器(避免组件销毁后仍执行)。
- 取消未完成的请求(避免资源浪费)。
- 解绑自定义事件。
javascript
new Vue({
el: '#app',
data() {
return { timer: null };
},
mounted() {
// 启动定时器
this.timer = setInterval(() => {
console.log('定时器执行中...');
}, 1000);
// 绑定事件
window.addEventListener('resize', this.handleResize);
},
beforeDestroy() {
// 清理定时器
clearInterval(this.timer);
// 移除事件监听
window.removeEventListener('resize', this.handleResize);
// 取消未完成的请求(假设使用axios)
if (this.source) {
this.source.cancel('组件销毁,取消请求');
}
},
methods: {
handleResize() { /* 处理窗口大小变化 */ }
}
});
5.2 destroyed
:销毁后
- 执行时机:组件实例销毁后调用,此时组件的所有资源已被释放。
- 特点 :
- 组件的响应式数据、事件监听、子组件等均已被销毁。
- 仍可访问
$el
,但DOM可能已被移除(取决于销毁方式)。
- 适用场景:极少使用,可用于最终的资源清理或日志记录。
javascript
new Vue({
destroyed() {
console.log('组件已销毁');
// 记录销毁日志
console.log('组件销毁时间:', new Date().toLocaleString());
}
});
六、特殊场景的钩子函数
除上述核心钩子外,Vue还提供了针对特殊场景的钩子函数:
6.1 keep-alive 相关钩子
keep-alive
用于缓存组件(避免频繁创建/销毁),搭配两个专属钩子:
activated
:缓存的组件被激活(显示)时调用。deactivated
:缓存的组件被停用(隐藏)时调用。
html
<!-- 缓存组件 -->
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
<script>
new Vue({
data() { return { currentComponent: 'ComponentA' }; },
components: {
ComponentA: {
template: '<div>组件A</div>',
activated() {
console.log('组件A被激活(从缓存中取出)');
// 可在此恢复状态(如刷新数据)
},
deactivated() {
console.log('组件A被停用(存入缓存)');
// 可在此暂停操作(如暂停视频播放)
}
}
}
});
</script>
6.2 错误捕获钩子:errorCaptured
用于捕获子组件抛出的错误(Vue 2.5+),返回false
可阻止错误向上传播:
javascript
new Vue({
errorCaptured(err, vm, info) {
console.error('捕获子组件错误:', err, '组件:', vm, '信息:', info);
// 返回false阻止错误冒泡到控制台
return false;
}
});
七、生命周期钩子的执行顺序与实战示例
7.1 单组件执行顺序
javascript
new Vue({
// 创建阶段
beforeCreate() { console.log('1. beforeCreate'); },
created() { console.log('2. created'); },
// 挂载阶段
beforeMount() { console.log('3. beforeMount'); },
mounted() { console.log('4. mounted'); },
// 更新阶段(触发条件:修改data)
beforeUpdate() { console.log('5. beforeUpdate'); },
updated() { console.log('6. updated'); },
// 销毁阶段(触发条件:调用$destroy()或v-if移除)
beforeDestroy() { console.log('7. beforeDestroy'); },
destroyed() { console.log('8. destroyed'); }
});
执行结果 :
初始化时:1→2→3→4
修改数据时:5→6
销毁时:7→8
7.2 父子组件执行顺序
父组件Parent
和子组件Child
的钩子执行顺序:
- 父
beforeCreate
→ 父created
→ 父beforeMount
- 子
beforeCreate
→ 子created
→ 子beforeMount
→ 子mounted
- 父
mounted
- (更新时)父
beforeUpdate
→ 子beforeUpdate
→ 子updated
→ 父updated
- (销毁时)父
beforeDestroy
→ 子beforeDestroy
→ 子destroyed
→ 父destroyed
结论:父组件等待子组件完成后,才会完成自身对应的阶段。
八、常见问题与避坑指南
8.1 避免在updated
中修改数据
javascript
// 错误示例:导致无限循环
updated() {
this.count++; // 修改data触发更新,再次调用updated,循环往复
}
解决方案 :若需根据更新后的数据调整状态,可使用$nextTick
或条件判断限制执行次数。
8.2 子组件mounted
晚于父组件mounted
父组件若需等待子组件挂载完成后执行操作(如获取子组件DOM),需使用$nextTick
或在子组件中通过事件通知父组件:
javascript
// 子组件
export default {
mounted() {
this.$emit('mounted'); // 通知父组件已挂载
}
};
// 父组件
<child-component @mounted="handleChildMounted"></child-component>
methods: {
handleChildMounted() {
console.log('子组件已挂载');
}
}
8.3 清理资源是重中之重
组件销毁时若未清理定时器、事件监听等,会导致内存泄漏(页面关闭前资源一直占用):
javascript
// 错误:未清理定时器
mounted() {
setInterval(() => { /* 操作 */ }, 1000); // 组件销毁后仍会执行
}
// 正确:在beforeDestroy中清理
beforeDestroy() {
clearInterval(this.timer);
}
九、Vue 3中的生命周期变化
Vue 3的Composition API中,生命周期钩子的使用方式有所调整,但核心逻辑一致:
setup()
:替代beforeCreate
和created
(在两者之间执行)。onBeforeMount
:对应beforeMount
。onMounted
:对应mounted
。onBeforeUpdate
:对应beforeUpdate
。onUpdated
:对应updated
。onBeforeUnmount
:对应beforeDestroy
。onUnmounted
:对应destroyed
。
javascript
// Vue 3 Composition API示例
import { onMounted, onBeforeUnmount } from 'vue';
export default {
setup() {
let timer;
onMounted(() => {
timer = setInterval(() => { /* 操作 */ }, 1000);
});
onBeforeUnmount(() => {
clearInterval(timer); // 清理资源
});
}
};
总结
- 精准控制流程 :在合适的阶段执行合适的操作(如
created
请求数据、mounted
操作DOM)。- 避免常见问题:如更新阶段的死循环、销毁阶段的内存泄漏。
- 优化组件性能 :合理利用
keep-alive
和缓存钩子,减少不必要的创建/销毁。
若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ