深入理解 Vue 的钩子函数(生命周期函数)
Vue 的钩子函数(生命周期函数)是 Vue 实例在不同阶段自动调用的函数。可以在 Vue 实例的创建、更新、销毁等阶段插入自己的逻辑。
钩子函数的作用
想象一下,Vue 实例的生命周期就像一个人的一生:出生 → 成长 → 衰老 → 死亡。钩子函数就是这些关键阶段的“时间点”。
组件加载时:初始化数据、请求接口数据
DOM 渲染后:操作 DOM 元素
组件销毁前:清除定时器、解绑事件
核心钩子函数详解
创建阶段(Creation)
-
beforeCreate
触发时机:实例刚被创建,数据观测和事件配置之前。
能做什么:此时无法访问data
和methods
,通常用于插件初始化。
不能做什么:操作数据或调用方法。 -
created
触发时机:实例创建完成,数据观测和事件配置已完成。
能做什么:可以访问data
和methods
,适合发起网络请求、初始化数据。
不能做什么:不要操作 DOM(此时 DOM 还未生成)。
export default {
data() {
return { message: "Hello" };
},
created() {
console.log(this.message); // 输出 "Hello"
this.fetchData(); // 调用方法请求数据
},
methods: {
fetchData() { /* ... */ }
}
}
2. 挂载阶段(Mounting)
-
beforeMount
触发时机:模板编译完成,但尚未将 DOM 渲染到页面。
能做什么:很少使用,一般用于服务端渲染。 -
mounted
触发时机:DOM 已渲染到页面,可以访问到真实的 DOM 元素。
能做什么:操作 DOM、使用第三方库(如图表库)。
确保子组件的 DOM 也已挂载(使用this.$nextTick
等待)。
mounted() {
this.$nextTick(() => {
const element = document.getElementById("my-element");
element.style.color = "red"; // 操作 DOM
});
}
3. 更新阶段(Updating)
-
beforeUpdate
触发时机:数据变化后,DOM 更新前。
能做什么:获取更新前的 DOM 状态(比如滚动位置)。 -
updated
触发时机:数据变化导致的 DOM 更新完成后。
能做什么:执行依赖 DOM 的操作(如重新计算布局)。
注意:避免在此钩子中修改数据,可能导致无限循环!
4. 销毁阶段(Destruction)
-
beforeDestroy
触发时机:实例销毁前。
能做什么:清理定时器、解绑全局事件、取消未完成的请求。 -
destroyed
触发时机:实例销毁后。
能做什么:很少使用,用于最后的清理工作。
beforeDestroy() {
clearInterval(this.timer); // 清除定时器
window.removeEventListener("resize", this.handleResize); // 解绑事件
}
假设一个组件从创建到销毁的完整流程,钩子函数的执行顺序如下:
beforeCreate
created
beforeMount
mounted
beforeUpdate
→updated
beforeDestroy
destroyed
<template>
<div class="lifecycle-demo">
<h2>Vue 生命週期鉤子演示</h2>
<div class="counter">
<p>計數器: {{ count }}</p>
<button @click="increment">增加</button>
</div>
<div class="message" v-if="showMessage">
{{ message }}
</div>
</div>
</template>
<script>
export default {
name: 'LifecycleDemo',
data() {
return {
count: 0,
message: '',
showMessage: false
}
},
// 創建實例前
beforeCreate() {
console.log('beforeCreate: 實例創建前')
// 此時 data 和 methods 都還未初始化
},
// 創建實例後
created() {
console.log('created: 實例創建後')
// 此時可以訪問 data 和 methods
this.message = '組件已創建'
},
// 掛載前
beforeMount() {
console.log('beforeMount: 掛載前')
// 此時模板已編譯,但還未渲染到 DOM
},
// 掛載後
mounted() {
console.log('mounted: 掛載後')
// 此時組件已完全掛載到 DOM
this.showMessage = true
},
// 更新前
beforeUpdate() {
console.log('beforeUpdate: 更新前')
// 數據更新時觸發,但 DOM 還未更新
},
// 更新後
updated() {
console.log('updated: 更新後')
// 數據和 DOM 都已更新
},
// 銷毀前
beforeDestroy() {
console.log('beforeDestroy: 銷毀前')
// 組件銷毀前,此時組件還可以使用
},
// 銷毀後
destroyed() {
console.log('destroyed: 銷毀後')
// 組件已完全銷毀
},
methods: {
increment() {
this.count++
}
}
}
</script>
<style scoped>
.lifecycle-demo {
max-width: 600px;
margin: 20px auto;
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
}
.counter {
margin: 20px 0;
padding: 15px;
background-color: #f5f5f5;
border-radius: 4px;
}
button {
padding: 8px 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
.message {
margin-top: 10px;
padding: 10px;
background-color: #e8f5e9;
border-radius: 4px;
color: #2e7d32;
}
</style>