[Medium] 📄 Vue Lifecycle Hooks
1. Please explain Vue lifecycle hooks (include Vue 2 & Vue 3)
請解釋 Vue 的生命週期鉤子(包含 Vue 2 和 Vue 3)
Vue 組件從創建到銷毀會經歷一系列的過程,在這些過程中會自動呼叫特定的函式,這些函式就是「生命週期鉤子」。理解生命週期對於掌握組件的行為非常重要。
Vue 生命週期圖解
建立階段 → 掛載階段 → 更新階段 → 銷毀階段
↓ ↓ ↓ ↓
Created Mounted Updated Unmounted
Vue 2 vs Vue 3 生命週期對照表
| Vue 2 (Options API) | Vue 3 (Options API) | Vue 3 (Composition API) | 說明 |
|---|---|---|---|
beforeCreate | beforeCreate | setup() | 組件實例初始化之前 |
created | created | setup() | 組件實例創建完成 |
beforeMount | beforeMount | onBeforeMount | 掛載到 DOM 之前 |
mounted | mounted | onMounted | 掛載到 DOM 之後 |
beforeUpdate | beforeUpdate | onBeforeUpdate | 資料更新前 |
updated | updated | onUpdated | 資料更新後 |
beforeDestroy | beforeUnmount | onBeforeUnmount | 組件卸載之前 |
destroyed | unmounted | onUnmounted | 組件卸載之後 |
activated | activated | onActivated | keep-alive 組件激活時 |
deactivated | deactivated | onDeactivated | keep-alive 組件停用時 |
errorCaptured | errorCaptured | onErrorCaptured | 捕獲子組件錯 誤時 |
1. 建立階段(Creation Phase)
beforeCreate / created
<script>
export default {
data() {
return {
message: 'Hello Vue',
};
},
beforeCreate() {
// ❌ 此時 data、methods 都還未初始化
console.log('beforeCreate');
console.log(this.message); // undefined
console.log(this.$el); // undefined
},
created() {
// ✅ 此時 data、computed、methods、watch 都已初始化
console.log('created');
console.log(this.message); // 'Hello Vue'
console.log(this.$el); // undefined(尚未掛載到 DOM)
// ✅ 適合在這裡發送 API 請求
this.fetchData();
},
methods: {
async fetchData() {
const response = await fetch('/api/data');
this.data = await response.json();
},
},
};
</script>
使用時機:
beforeCreate:很少使用,通常用於插件開發created:- ✅ 發送 API 請求
- ✅ 初始化非響應式的資料
- ✅ 設定事件監聽器
- ❌ 無法操作 DOM(尚未掛載)
2. 掛載階段(Mounting Phase)
beforeMount / mounted
<template>
<div ref="myElement">
<h1>{{ title }}</h1>
<canvas ref="myCanvas"></canvas>
</div>
</template>
<script>
export default {
data() {
return {
title: 'Vue Lifecycle',
};
},
beforeMount() {
// ❌ 此時虛擬 DOM 已建立,但尚未渲染到真實 DOM
console.log('beforeMount');
console.log(this.$el); // 存在,但內容是舊的(如果有的話)
console.log(this.$refs.myElement); // undefined
},
mounted() {
// ✅ 此時組件已掛載到 DOM,可以操作 DOM 元素
console.log('mounted');
console.log(this.$el); // 真實的 DOM 元素
console.log(this.$refs.myElement); // 可以存取 ref
// ✅ 適合在這裡操作 DOM
this.initCanvas();
// ✅ 適合在這裡使用第三方 DOM 套件
this.initChart();
},
methods: {
initCanvas() {
const canvas = this.$refs.myCanvas;
const ctx = canvas.getContext('2d');
// 繪製 canvas...
},
initChart() {
// 初始化圖表套件(如 Chart.js, ECharts)
new Chart(this.$refs.myCanvas, {
type: 'bar',
data: {
/* ... */
},
});
},
},
};
</script>
使用時機:
beforeMount:很少使用mounted:- ✅ 操作 DOM 元素
- ✅ 初始化第三方 DOM 套件(如圖表、地圖)
- ✅ 設定需要 DOM 的事件監聽器
- ✅ 啟動計時器
- ⚠️ 注意:子組件的
mounted會在父組件的mounted之前執行
3. 更新階段(Updating Phase)
beforeUpdate / updated
<template>
<div>
<p>計數:{{ count }}</p>
<button @click="count++">增加</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0,
};
},
beforeUpdate() {
// ✅ 資料已更新,但 DOM 尚未更新
console.log('beforeUpdate');
console.log('data count:', this.count); // 新的值
console.log('DOM count:', this.$el.querySelector('p').textContent); // 舊的值
// 可以在這裡存取更新前的 DOM 狀態
},
updated() {
// ✅ 資料和 DOM 都已更新
console.log('updated');
console.log('data count:', this.count); // 新的值
console.log('DOM count:', this.$el.querySelector('p').textContent); // 新的值
// ⚠️ 注意:不要在這裡修改資料,會導致無限循環
// this.count++; // ❌ 錯誤!會導致無限更新
},
};
</script>
使用時機:
beforeUpdate:需要在 DOM 更新前存取舊的 DOM 狀態updated:- ✅ DOM 更新後需要執行的操作(如重新計算元素尺寸)
- ❌ 不要在這裡修改資料,會導致無限更新循環
- ⚠️ 如果需要在資料變化後執行操作,建議使用
watch或nextTick
4. 銷毀階段(Unmounting Phase)
beforeUnmount / unmounted (Vue 3) / beforeDestroy / destroyed (Vue 2)
<script>
export default {
data() {
return {
timer: null,
ws: null,
};
},
mounted() {
// 設定計時器
this.timer = setInterval(() => {
console.log('計時器執行中...');
}, 1000);
// 建立 WebSocket 連接
this.ws = new WebSocket('ws://example.com');
this.ws.onmessage = (event) => {
console.log('收到訊息:', event.data);
};
// 設定事件監聽器
window.addEventListener('resize', this.handleResize);
document.addEventListener('click', this.handleClick);
},
beforeUnmount() {
// Vue 3 使用 beforeUnmount
// Vue 2 使用 beforeDestroy
console.log('beforeUnmount');
// 組件即將被銷毀,但還可以存取資料和 DOM
},
unmounted() {
// Vue 3 使用 unmounted
// Vue 2 使用 destroyed
console.log('unmounted');
// ✅ 清理計時器
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
// ✅ 關閉 WebSocket 連接
if (this.ws) {
this.ws.close();
this.ws = null;
}
// ✅ 移除事件監聽器
window.removeEventListener('resize', this.handleResize);
document.removeEventListener('click', this.handleClick);
},
methods: {
handleResize() {
console.log('視窗大小改變');
},
handleClick() {
console.log('點擊事件');
},
},
};
</script>
使用時機:
beforeUnmount/beforeDestroy:很少使用unmounted/destroyed:- ✅ 清理計時器(
setInterval、setTimeout) - ✅ 移除事件監聽器
- ✅ 關閉 WebSocket 連接
- ✅ 取消未完成的 API 請求
- ✅ 清理第三方套件實例
- ✅ 清理計時器(