Vue3 教學:Lifecycle Hooks(生命週期)— onMounted、onUpdated、onUnmounted
簡介
在 Vue 3 中,元件的生命週期是一連串由 Vue 自動觸發的階段,讓開發者可以在適當的時機注入自訂邏輯。掌握 onMounted、onUpdated、onUnmounted 三個最常用的 Composition API 生命週期鉤子(hook),不僅能讓程式碼更具可讀性,也能避免因為時機不對而產生的效能或錯誤問題。
本篇文章將以 淺顯易懂 的方式說明這三個 Hook 的運作原理、常見使用情境,以及實務上應該注意的陷阱與最佳實踐,幫助初學者快速上手,同時提供給中階開發者作為日常開發的參考手冊。
核心概念
1. onMounted – 元件掛載完成後執行
onMounted 會在 DOM 元素真正渲染到頁面上 後呼叫,此時可以安全地存取 $el、操作第三方插件、或發送 API 請求取得資料。
import { ref, onMounted } from 'vue'
export default {
setup() {
const message = ref('Loading...')
// 只會在第一次掛載時執行一次
onMounted(async () => {
// 假設有一個取得歡迎訊息的 API
const res = await fetch('/api/welcome')
const data = await res.json()
message.value = data.text
})
return { message }
}
}
重點:
onMounted只會在首次掛載時觸發,若元件被v-if再次顯示,仍會重新呼叫一次。
2. onUpdated – 元件更新(重新渲染)後執行
當 響應式資料變更,導致模板重新渲染時,onUpdated 會在 DOM 更新完成 後被呼叫。這個 Hook 常用於同步外部 UI(如圖表、動畫)或偵測特定屬性的變化。
import { ref, watch, onUpdated } from 'vue'
export default {
setup() {
const count = ref(0)
// 每次 count 改變後,DOM 會重新渲染
onUpdated(() => {
console.log('DOM 已更新,最新的 count 為', count.value)
})
// 也可以配合 watch 做更細緻的控制
watch(count, (newVal, oldVal) => {
console.log(`count 從 ${oldVal} 變成 ${newVal}`)
})
const inc = () => count.value++
return { count, inc }
}
}
提示:
onUpdated會在每一次渲染後執行,若頻繁更新可能會影響效能,請只在需要同步外部資源時使用。
3. onUnmounted – 元件銷毀前執行
當元件從 DOM 中移除(例如 v-if 為 false、路由切換等)時,onUnmounted 讓你清理資源:移除事件監聽、關閉 WebSocket、銷毀第三方插件等。
import { ref, onMounted, onUnmounted } from 'vue'
export default {
setup() {
const timer = ref(null)
onMounted(() => {
// 每秒更新一次時間顯示
timer.value = setInterval(() => {
console.log('tick')
}, 1000)
})
onUnmounted(() => {
// 必須在此清除定時器,避免記憶體洩漏
clearInterval(timer.value)
console.log('元件已銷毀,定時器已清除')
})
}
}
注意:即使在
v-if切換 時元件會被銷毀,onUnmounted仍會正確觸發,確保所有副作用都有機會被釋放。
4. 綜合範例:結合三個 Hook 的完整流程
以下範例示範一個「即時搜尋」元件,使用 onMounted 初始化搜尋 API、onUpdated 監測輸入變化、onUnmounted 清除 debounce 計時器。
import { ref, onMounted, onUpdated, onUnmounted, watch } from 'vue'
export default {
setup() {
const query = ref('')
const results = ref([])
let debounceTimer = null
// 初始化時先載入預設資料
onMounted(async () => {
const res = await fetch('/api/search?keyword=')
results.value = await res.json()
})
// 當 query 改變時,使用 debounce 減少 API 呼叫次數
watch(query, (newVal) => {
clearTimeout(debounceTimer)
debounceTimer = setTimeout(async () => {
const res = await fetch(`/api/search?keyword=${encodeURIComponent(newVal)}`)
results.value = await res.json()
}, 300) // 300ms 防抖
})
// 每次搜尋結果更新後,印出訊息(示範 onUpdated)
onUpdated(() => {
console.log('搜尋結果已更新,筆數:', results.value.length)
})
// 元件銷毀時清除 debounce 計時器
onUnmounted(() => {
clearTimeout(debounceTimer)
})
return { query, results }
}
}
常見陷阱與最佳實踐
| 陷阱 | 可能的問題 | 解決方案 / 實務建議 |
|---|---|---|
在 onMounted 中直接操作 DOM,卻忘記在 SSR(伺服器端渲染)環境下會失效 |
產生 document is not defined 錯誤 |
使用 if (import.meta.env.SSR) return 或只在客戶端執行的條件式包住程式碼 |
onUpdated 內做大量計算,導致渲染卡頓 |
每次資料變動都觸發,效能下降 | 盡量把重度運算搬到 watch 或 computed,僅在 onUpdated 做 DOM 同步 |
忘記在 onUnmounted 清除事件或計時器 |
記憶體洩漏、意外觸發多次回呼 | 統一管理:所有 addEventListener、setInterval、WebSocket 都要在 onUnmounted 內對應 removeEventListener、clearInterval、close |
在 setup 直接使用 this |
this 為 undefined(Composition API) |
改用 ref、reactive、computed,或在 <script setup> 中直接使用變數 |
在 watch 裡同時使用 onUpdated,造成重複執行 |
雙重觸發,難以追蹤 | 只保留一個機制:若只是監控資料變化,使用 watch;若需要等 DOM 完全更新後再執行,使用 onUpdated |
最佳實踐小結
- 只在需要時使用
onUpdated:若只是要在資料變化時執行,watch更合適。 - 保持 Hook 的單一職責:
onMounted用於初始化、onUpdated用於同步外部 UI、onUnmounted用於清理。 - 結合 TypeScript:在
setup中為ref、computed加上型別,有助於避免因錯誤的資料型別導致的 runtime 錯誤。 - 測試生命週期:使用 Vue Test Utils 的
mount、unmount,確保onMounted、onUnmounted的副作用真的被觸發與清除。
實際應用場景
| 場景 | 使用的 Hook | 為何選擇 |
|---|---|---|
| 圖表套件(如 Chart.js) | onMounted 初始化圖表、onUpdated 更新資料、onUnmounted 銷毀圖表實例 |
圖表需要 DOM 實體,且資料變動時要重新渲染 |
| WebSocket 連線 | onMounted 建立連線、onUnmounted 關閉連線 |
防止連線遺留在背景,造成資源浪費 |
| 自動聚焦表單欄位 | onMounted 取得 ref 後呼叫 .focus() |
元件渲染完畢才能操作真實的 input 元素 |
| 第三方 UI 套件(如 Bootstrap modal) | onMounted 初始化 modal、onUpdated 當內容改變時重新調整高度、onUnmounted 銷毀 modal |
需要在 DOM 完全就緒後才能正確計算尺寸 |
| SEO / SSR 頁面 | onMounted 僅在客戶端執行資料抓取,避免在 SSR 時重複請求 |
確保伺服器端渲染不會因為 fetch 而卡住 |
總結
onMounted:元件首次掛載完成後執行,適合 初始化、DOM 操作、API 請求。onUpdated:每次資料變動且 DOM 重新渲染後執行,適合 同步外部 UI 或 偵測渲染結果。onUnmounted:元件即將銷毀前執行,務必 清理所有副作用(事件、計時器、連線)。
掌握這三個生命週期鉤子,能讓 Vue 3 元件在 效能、可維護性、資源管理 上都有更佳的表現。未來在實作更複雜的功能時,只要遵循「初始化 → 更新 → 清理」的流程,就能寫出乾淨、可靠的程式碼。祝開發順利,期待你在 Vue 世界裡寫出更多優雅的元件!