Vue 3 生命週期完全指南:Composition API 鉤子的觸發順序與效能優化
在開發 Vue 3 應用程式時,理解元件從建立、掛載、更新到銷毀的整個生命週期(Lifecycle),是編寫高效能、低耦合程式碼的關鍵基本功。
Vue 3 引入了 Composition API,使得生命週期鉤子(Lifecycle Hooks)的宣告方式與 Vue 2 有了顯著的不同。本文將帶您全面掌握 Vue 3 生命週期的運作機制以及實戰中的效能優化心法。
生命週期流程
這裡展示了完整的 Vue 生命週期流程,每個 Vue 元件從被建立到被銷毀,會歷經一系列的生命週期階段。Vue 在每個關鍵時刻都提供了對應的生命週期函式,讓您可以在特定時機插入自訂邏輯。
在 Composition API 中,所有生命週期鉤子都在 setup 內部呼叫,以 on 開頭命名。
【建立階段】
↓
setup() ← 所有 Composition API 邏輯在這裡(無法存取 DOM)
↓
【掛載】
↓
onBeforeMount() ← DOM 尚未建立,很少使用
↓
onMounted() ← DOM 已建立,最常用
↓
【資料更新】
↓
onBeforeUpdate() ← DOM 更新前
↓
onUpdated() ← DOM 更新後
↓
【銷毀階段】
↓
onBeforeUnmount() ← 元件即將銷毀
↓
onUnmounted() ← 元件已銷毀,清理資源的地方其中 掛載階段只會發生一次,更新階段則會隨著響應式資料的變動反覆觸發,而 銷毀階段 發生在元件被移除時,例如:路由切換、v-if 條件不再成立、或 v-for 清單中的項目被移除。
生命週期語法使用
生命週期鉤子必須從 vue 匯入,並在 setup 中 同步呼叫。以下範例把所有鉤子都印出來,可以直接貼到元件裡觀察觸發順序:
<script setup>
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
} from "vue";
console.log("setup");
onBeforeMount(() => {
console.log("onBeforeMount");
});
onMounted(() => {
console.log("onMounted");
});
onBeforeUpdate(() => {
console.log("onBeforeUpdate");
});
onUpdated(() => {
console.log("onUpdated");
});
onBeforeUnmount(() => {
console.log("onBeforeUnmount");
});
onUnmounted(() => {
console.log("onUnmounted");
});
</script>鉤子不能寫在非同步流程裡
Vue 是靠 目前正在執行的元件實體 來決定鉤子要註冊給誰,因此鉤子只能在 setup 中同步呼叫。寫在 setTimeout 或 await 之後,Vue 已經找不到對應的元件,註冊會直接失效。
同一個鉤子 可以註冊多次,Vue 會依註冊順序依序執行。這個特性讓每個 Composable 都能自己管理自己的掛載與清理,不必把邏輯集中到元件裡。
各階段適合做什麼
| 階段 | 適合做的事 | 要避免的事 |
|---|---|---|
setup | 建立 ref、computed、註冊 watch、發送不需要 DOM 的 API 請求 | 存取 template 中的 DOM 元素或 ref 綁定的節點 |
onBeforeMount | 幾乎不需要用到,偶爾用於掛載前的最後一次資料整理 | 操作 DOM,此時尚未產生 |
onMounted | 操作實體 DOM、初始化第三方套件、建立事件監聽與計時器 | 把每次資料變動都要做的事寫在這裡,它只執行一次 |
onBeforeUpdate | 記錄更新前的 DOM 狀態,例如:捲軸位置、輸入框游標 | 修改響應式狀態 |
onUpdated | 讀取更新後的 DOM 尺寸或位置 | 修改響應式狀態,會造成無窮更新迴圈 |
onBeforeUnmount | 需要讀取 DOM 或元件狀態的清理工作 | 建立新的訂閱或請求 |
onUnmounted | 解除事件監聽、清除計時器、關閉連線 | 存取已被移除的 DOM |
想直接看這些階段的完整程式碼,可以接著閱讀 核心生命週期實戰場景。
結論:生命週期效能優化最佳實踐
為了確保 Vue 3 應用程式具有極佳的效能與搜尋引擎友善度,請遵循以下核心原則:
- 不要在 onUpdated 中修改響應式狀態:在 onUpdated 中修改資料會再次觸發資料變更,進而重新觸發更新,這極易導致 無窮更新迴圈,造成網頁卡死。真的需要依資料變動執行動作時,請改用
watch。 - 善用非同步元件降低初始載入時間:結合生命週期,非核心元件可以使用
defineAsyncComponent()進行延遲載入,優化 Google Core Web Vitals 的 LCP(最大內容繪製)指標。 - 精準控管副作用:在生命週期之外,例如:非同步流程中建立的監聽器或訂閱,必須在 onUnmounted 中確實呼叫其銷毀函數,以免造成效能浪費。
- 只在需要 DOM 時才等到 onMounted:不依賴 DOM 的請求寫在
setup就能更早發出,縮短使用者看到資料的等待時間。
同場加映:Vue 2 vs Vue 3 生命週期對比
在 Vue 3 的 Composition API 中,程式碼的核心入口是 setup() 函數。因此,Vue 2 中的 beforeCreate 和 created 階段,在 Vue 3 中直接由 setup() 函數本身取代,無需任何額外的鉤子。
其他的生命週期鉤子則加上了 on 前綴,並且需要從 vue 中明確匯入:
| Vue 2 Options API | Vue 3 Composition API | 觸發時機與用途 |
|---|---|---|
beforeCreate / created | setup() | 元件初始化,建立響應式狀態。此時 DOM 尚未產生。 |
beforeMount | onBeforeMount | 組件即將被掛載到 DOM 之前執行。 |
mounted | onMounted | 組件已成功掛載到 DOM。適合執行 API 請求、DOM 操作。 |
beforeUpdate | onBeforeUpdate | 響應式資料改變,但 DOM 尚未重新渲染。 |
updated | onUpdated | 資料改變且 DOM 已完成更新。避免在此修改狀態以防無窮迴圈。 |
beforeDestroy | onBeforeUnmount | 元件即將被銷毀(卸載)前。適合清除定時器、解除全域事件。 |
destroyed | onUnmounted | 元件已完全銷毀,所有專屬監聽器與子組件皆已釋放。 |
常見問題
Vue 3 為什麼沒有 beforeCreate 與 created?
因為 <script setup> 本身就等於這兩個階段。setup 的程式碼在元件實體建立時同步執行,響應式狀態也在此建立,所以不需要額外的鉤子。
API 請求要寫在 setup 還是 onMounted?
兩者都可以發送請求,差別在於是否需要 DOM。若請求結果只是填進響應式狀態交給模板渲染,寫在 setup 可以更早發出;若需要在拿到資料後操作實體 DOM,例如:初始化圖表,就要寫在 onMounted。
為什麼不能在 onUpdated 裡修改響應式狀態?
onUpdated 在 DOM 更新後觸發,此時修改狀態會再次觸發更新,更新完又進入 onUpdated,形成 無窮更新迴圈 導致頁面卡死。需要依資料變動執行動作時,應改用 watch。
onBeforeUnmount 與 onUnmounted 該用哪一個清理資源?
需要讀取即將消失的 DOM 或元件狀態時用 onBeforeUnmount,此時元件仍然完整。單純解除計時器、事件監聽或連線這類與 DOM 無關的清理,寫在 onUnmounted 也可以。
生命週期鉤子可以寫在非同步回呼裡嗎?
不行。鉤子必須在 setup 中同步呼叫,Vue 才能把它綁定到目前的元件實體。寫在 setTimeout 或 await 之後註冊會失效,開發模式下還會出現警告。
同一個鉤子可以註冊多次嗎?
可以。多次呼叫 onMounted 會依註冊順序全部執行,這也是 Composable 能各自管理自己的生命週期而不互相干擾的原因。