本文 AI 產出,尚未審核
Vue 3 生命周期對應表(Options API vs Composition API)
簡介
在 Vue 3 中,生命週期(Lifecycle) 是每個元件從建立、渲染、更新到銷毀的完整過程。了解生命週期不僅能讓我們在正確的時機執行初始化或清除工作,還能避免資源洩漏、效能瓶頸等常見問題。
Vue 3 同時支援 Options API 與 Composition API,兩者雖然寫法不同,但底層觸發的生命週期鉤子是一樣的。本篇文章將提供一張完整的對應表,說明兩種寫法的等價關係,並透過實作範例展示如何在實務中正確使用。
核心概念
1. 什麼是生命週期鉤子?
生命週期鉤子是 Vue 在元件不同階段自動呼叫的函式,開發者可以在這些鉤子裡加入自訂邏輯。常見的階段包括:
| 階段 | 說明 |
|---|---|
| 創建 (creation) | 元件實例被建立,資料觀測 (reactivity) 設定完成,但尚未掛載到 DOM。 |
| 掛載 (mount) | 元件的模板被編譯成實際的 DOM,並插入到頁面上。 |
| 更新 (update) | 當響應式資料變化時,重新渲染相關的 DOM。 |
| 銷毀 (unmount) | 元件被移除,所有監聽、計時器等需要手動清理的資源在此階段釋放。 |
2. Options API vs Composition API
- Options API:透過
data、methods、computed、watch、created、mounted…等選項來描述元件。適合快速上手、結構清晰的情境。 - Composition API:在
setup()函式內使用ref、reactive、watch、onMounted…等函式,將相關邏輯彈性組合。適合大型專案、重複使用邏輯的情況。
3. 生命周期對應表
| Options API 鉤子 | Composition API 對應函式 | 說明 |
|---|---|---|
beforeCreate |
無直接對應(在 setup 前) |
這個階段已經很少使用,若需要可在 setup 前自行執行初始化程式碼。 |
created |
onCreated(非官方)實際上在 setup 內即可執行 |
於 setup 內直接寫邏輯即可,因為此時已完成響應式建立。 |
beforeMount |
onBeforeMount |
元件即將掛載到 DOM 前。 |
mounted |
onMounted |
元件已成功掛載,DOM 可直接存取。 |
beforeUpdate |
onBeforeUpdate |
資料變更即將觸發重新渲染前。 |
updated |
onUpdated |
DOM 已完成更新。 |
beforeUnmount |
onBeforeUnmount |
元件即將被移除前。 |
unmounted |
onUnmounted |
元件已從 DOM 移除,可清理資源。 |
errorCaptured |
onErrorCaptured |
捕獲子元件錯誤。 |
renderTracked |
onRenderTracked |
開發除錯用,追蹤哪些響應式來源觸發渲染。 |
renderTriggered |
onRenderTriggered |
開發除錯用,追蹤哪些變更觸發渲染。 |
小提醒:Composition API 的生命週期函式必須在
setup()內呼叫,且只能在同一個setup作用域中使用。
程式碼範例
以下示範同一個簡易計數器元件,分別用 Options API 與 Composition API 實作,並以 對應表 中的鉤子說明每個階段的用途。
1. Options API 範例
<template>
<button @click="increment">{{ count }}</button>
</template>
<script>
export default {
name: 'CounterOption',
data() {
return { count: 0 }
},
// ---------- 生命週期 ----------
beforeMount() {
console.log('【Options】beforeMount:即將掛載')
},
mounted() {
console.log('【Options】mounted:已掛載,DOM 可使用')
},
beforeUpdate() {
console.log('【Options】beforeUpdate:即將更新')
},
updated() {
console.log('【Options】updated:更新完成')
},
beforeUnmount() {
console.log('【Options】beforeUnmount:即將卸載')
},
unmounted() {
console.log('【Options】unmounted:已卸載')
},
// ---------- 方法 ----------
methods: {
increment() {
this.count++
}
}
}
</script>
說明
- 在
mounted()中,我們可以安全地存取this.$el(DOM 節點)。beforeUpdate/updated常用於 偵測資料變化前後 的額外處理,例如動畫過渡。
2. Composition API 範例
<template>
<button @click="increment">{{ count }}</button>
</template>
<script setup>
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'
const count = ref(0)
// ---------- 生命週期 ----------
onBeforeMount(() => {
console.log('【Composition】onBeforeMount:即將掛載')
})
onMounted(() => {
console.log('【Composition】onMounted:已掛載,DOM 可使用')
// 例如:取得 DOM 高度
// const height = $el.offsetHeight
})
onBeforeUpdate(() => {
console.log('【Composition】onBeforeUpdate:即將更新')
})
onUpdated(() => {
console.log('【Composition】onUpdated:更新完成')
})
onBeforeUnmount(() => {
console.log('【Composition】onBeforeUnmount:即將卸載')
})
onUnmounted(() => {
console.log('【Composition】onUnmounted:已卸載')
})
// ---------- 方法 ----------
function increment() {
count.value++
}
</script>
說明
setup內的所有變數(如count)必須使用ref或reactive包裝,才能保持響應式。- 生命週期函式直接在
setup中呼叫,語意上更接近「在此階段執行…」的直觀感受。
3. 共享邏輯:自訂 Hook(Composition API)
若多個元件需要相同的「掛載時發送 API」邏輯,我們可以抽成一個可重用的 composable:
// useFetchData.js
import { ref, onMounted, onUnmounted } from 'vue'
export function useFetchData(url) {
const data = ref(null)
const loading = ref(true)
let abortCtrl = null
const fetchData = async () => {
abortCtrl = new AbortController()
try {
const res = await fetch(url, { signal: abortCtrl.signal })
data.value = await res.json()
} finally {
loading.value = false
}
}
onMounted(fetchData)
// 防止元件卸載時仍在等待回應
onUnmounted(() => abortCtrl?.abort())
return { data, loading }
}
在元件中使用:
<script setup>
import { useFetchData } from './useFetchData'
const { data, loading } = useFetchData('https://api.example.com/items')
</script>
<template>
<div v-if="loading">載入中…</div>
<pre v-else>{{ data }}</pre>
</template>
重點:
onMounted與onUnmounted直接在 composable 內使用,使得 生命週期與業務邏輯緊密耦合,且可在多個元件間共享。
4. 監聽路由變化(onBeforeRouteUpdate)
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
import { ref } from 'vue'
const page = ref(1)
onBeforeRouteUpdate((to, from, next) => {
// 當路由參數變動時,重新設定 page
page.value = Number(to.query.page) || 1
next()
})
</script>
說明:此為 Vue Router 提供的額外生命週期鉤子,與 Vue 本身的
onBeforeUpdate不同,專門用於 路由變化 前的處理。
5. 錯誤捕獲(errorCaptured / onErrorCaptured)
<script setup>
import { onErrorCaptured } from 'vue'
onErrorCaptured((err, instance, info) => {
console.error('捕獲錯誤:', err, info)
// 回傳 false 可阻止錯誤向上冒泡
return false
})
</script>
實務:在大型應用中,可在根元件使用此鉤子統一記錄錯誤,或顯示全局錯誤提示。
常見陷阱與最佳實踐
| 陷阱 | 可能的結果 | 解決方案 |
|---|---|---|
在 setup 之外呼叫生命週期函式 |
會拋出 Missing current instance 錯誤 |
確保所有 onXxx 只能在 setup 內或 composable 中呼叫 |
在 mounted 裡直接更改 ref |
會觸發額外一次渲染,造成效能浪費 | 若需要在掛載時一次性設定多個值,建議使用 nextTick 或在 setup 中直接初始化 |
忘記在 onUnmounted 清理訂閱 |
記憶體洩漏、事件觸發錯誤 | 所有 watch、eventListener、setInterval、fetch 的 abort controller 必須在 onUnmounted 中解除 |
在 created 中使用 this.$el |
this.$el 為 null,因為尚未掛載 |
必須改為 mounted 或 onMounted |
| 混用 Options 與 Composition,導致重複生命週期 | 同一階段的程式碼執行兩次 | 在同一元件內盡量選擇單一 API,或確保兩者的邏輯不衝突 |
最佳實踐
- 盡量在
setup中完成初始化:除非真的需要存取 DOM,否則把資料、計算屬性、watcher 放在setup,減少created/mounted的使用。 - 使用 composable 抽離共用邏輯:讓生命週期與業務程式碼分離,提升可測試性與可維護性。
- 在
onUnmounted中清理所有副作用:包括watchEffect、第三方庫的事件、定時器等。 - 使用 TypeScript 時,為
onXxx加上明確的型別:可避免因錯誤回傳值(如return false)而產生的行為差異。
實際應用場景
| 場景 | 為何需要生命週期 | 建議寫法 |
|---|---|---|
| 首次載入即發送 API | 必須在 DOM 完全掛載前取得資料,避免 UI 閃爍 | onMounted(Composition)或 mounted(Options) |
| 根據路由參數動態載入資料 | 路由變更時需重新請求 | watch 搭配 onMounted,或 onBeforeRouteUpdate |
| 使用第三方 UI 元件(如 Chart.js) | 必須在 <canvas> 元素掛載後才能初始化圖表 |
onMounted 初始化圖表,onUnmounted 銷毀圖表實例 |
| 全局錯誤監控 | 捕捉子元件的異常,統一上報 | 在根元件使用 onErrorCaptured |
| 動畫過渡 | 在更新前後執行 CSS/JS 動畫 | onBeforeUpdate / onUpdated 或 onBeforeMount / onMounted |
範例:在 Dashboard 中使用 Chart.js
<template><canvas ref="chartCanvas"></canvas></template> <script setup> import { ref, onMounted, onUnmounted } from 'vue' import Chart from 'chart.js/auto' const chartCanvas = ref(null) let chartInstance = null onMounted(() => { chartInstance = new Chart(chartCanvas.value, { /* config */ }) }) onUnmounted(() => { chartInstance?.destroy() }) </script>
總結
- Vue 3 的生命週期在 Options API 與 Composition API 之間是一一對應的,只是呼叫方式不同。
- 透過對應表,我們可以快速將舊有 Options 寫法遷移至 Composition,或在新專案中直接使用
setup+onXxx。 - 正確使用生命週期能讓 初始化、清理、錯誤處理 等工作更有條理,同時避免資源洩漏與不必要的重渲染。
- 把重複的生命週期邏輯抽成 composable,不僅提升程式碼可讀性,也方便單元測試與團隊協作。
掌握這張對應表與最佳實踐,你就能在 Vue 3 中自在切換兩種 API,寫出結構清晰、效能優化且易於維護的元件!祝開發順利 🚀