本文 AI 產出,尚未審核
Vue3 生命週期 – onBeforeMount / onBeforeUpdate / onBeforeUnmount
簡介
在 Vue 3 中,生命週期 (Lifecycle) 是每個元件在建立、更新、銷毀過程中會依序觸發的一系列鉤子函式。了解這些鉤子不僅能讓我們在正確的時機插入自訂邏輯,還能避免資源洩漏、效能瓶頸等常見問題。
本單元聚焦於 onBeforeMount、onBeforeUpdate、onBeforeUnmount 三個「即將」階段的 Hook。它們分別在「掛載前」、「更新前」與「銷毀前」被呼叫,提供我們最後一次機會去準備或清理元件。對於需要在畫面渲染前完成資料預處理、在狀態變更前做比對、或在離開頁面前釋放資源的情境,這三個 Hook 是不可或缺的工具。
核心概念
1. onBeforeMount
- 觸發時機:在 Vue 完成 虛擬 DOM 的建立,但還未把實際 DOM 插入到頁面上時。
- 典型用途:
- 取得或計算 首次渲染所需的資料,但不想阻塞
setup。 - 設定 第三方 UI 元件(如圖表、地圖)需要的容器尺寸,確保容器已存在於 DOM。
- 取得或計算 首次渲染所需的資料,但不想阻塞
<script setup>
import { ref, onBeforeMount } from 'vue'
const chartData = ref([])
// 假設有一個取得圖表資料的 API
async function fetchChartData() {
const res = await fetch('/api/chart')
chartData.value = await res.json()
}
// 在掛載前先取得資料,避免畫面閃爍
onBeforeMount(() => {
// 這裡可以使用 async/await,但 onBeforeMount 本身不支援返回 Promise
// 因此直接呼叫非同步函式
fetchChartData()
})
</script>
<template>
<div id="chart-container">
<!-- 這裡的圖表會在資料取得後自動渲染 -->
</div>
</template>
小技巧:如果需要在
onBeforeMount中使用await,可將非同步函式抽離,然後在 Hook 內直接呼叫,Vue 不會等待其完成,渲染會在資料回來後自動更新。
2. onBeforeUpdate
- 觸發時機:當 響應式狀態 變動,導致元件即將重新渲染 之前。
- 典型用途:
- 比對舊值與新值,決定是否真的需要執行昂貴的計算或 API 請求。
- 暫存 UI 狀態(如滾動位置、焦點),以便在更新後恢復。
<script setup>
import { ref, onBeforeUpdate, watch } from 'vue'
const searchTerm = ref('')
const results = ref([])
// 每次搜尋前先保存當前的滾動位置
let previousScrollY = 0
onBeforeUpdate(() => {
// 只在 searchTerm 改變時執行
if (searchTerm.value !== previousSearch) {
previousScrollY = window.scrollY // 保存滾動位置
}
})
// 監聽 searchTerm 變化,發送搜尋請求
watch(searchTerm, async (newVal, oldVal) => {
previousSearch = oldVal
const res = await fetch(`/api/search?q=${newVal}`)
results.value = await res.json()
})
</script>
<template>
<input v-model="searchTerm" placeholder="輸入關鍵字搜尋…" />
<ul>
<li v-for="item in results" :key="item.id">{{ item.name }}</li>
</ul>
</template>
注意:
onBeforeUpdate只會在 實際的渲染 前被呼叫。如果在同一次更新週期內多次改變同一個響應式屬性,Hook 仍只執行一次。
3. onBeforeUnmount
- 觸發時機:元件即將從 DOM 中移除,銷毀前。
- 典型用途:
- 清除事件監聽、取消訂閱、關閉 WebSocket、清除計時器。
- 儲存使用者離開前的狀態(如表單草稿),方便下次回來時還原。
<script setup>
import { ref, onBeforeUnmount } from 'vue'
const timerId = ref(null)
const counter = ref(0)
// 每秒遞增計數器
function startTimer() {
timerId.value = setInterval(() => {
counter.value++
}, 1000)
}
// 元件掛載時啟動計時器
startTimer()
// 在元件被銷毀前清除計時器,防止記憶體洩漏
onBeforeUnmount(() => {
clearInterval(timerId.value)
console.log('Timer cleared, component is about to be unmounted.')
})
</script>
<template>
<p>計時器:{{ counter }} 秒</p>
</template>
最佳實踐:所有在
setup中建立的外部資源(例如addEventListener、setTimeout、第三方庫的實例)**必須在onBeforeUnmount中對應清理,否則即使元件已被移除,仍會持續佔用記憶體或觸發不必要的回呼。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
在 onBeforeMount 使用 await |
onBeforeMount 不會等待 Promise 完成,導致資料仍在渲染後才回來,出現閃爍或錯誤 UI。 |
把非同步邏輯抽成獨立函式,或改用 onMounted(會等 DOM 完全掛載)配合 await。 |
在 onBeforeUpdate 內直接改變響應式資料 |
會觸發無限更新迴圈,因為每次改變都會再觸發 onBeforeUpdate。 |
僅讀取或使用 nextTick 延遲變更,或在 watch 中處理資料變更。 |
| 忘記清理計時器或事件監聽 | 元件被銷毀後仍持續執行,造成記憶體洩漏或意外行為。 | 在 onBeforeUnmount 中統一清除所有外部資源;可將清理函式集中管理(例如返回一個 dispose 函式)。 |
在 onBeforeUnmount 中使用已被釋放的變數 |
若變數在其他地方已被設定為 null,可能拋出錯誤。 |
確保清理程式碼具備容錯(如 if (timerId) clearInterval(timerId)). |
過度依賴 onBeforeUpdate 進行資料請求 |
每次 UI 更新都發起請求,會造成大量不必要的網路流量。 | 使用 watch 或 debounce 來限制請求頻率,僅在關鍵資料變化時才呼叫 API。 |
最佳實踐小結
- 只在必要時使用:若不需要在掛載前就取得資料,直接在
setup或onMounted處理即可。 - 保持 Hook 純粹:Hook 應只負責「何時」執行,而非「怎麼」實作。具體邏輯建議抽成可重用的函式。
- 統一清理:建立一個
cleanup陣列,將所有需要在onBeforeUnmount執行的函式推入,最後一次迭代執行,提升可維護性。
import { onBeforeUnmount } from 'vue'
const disposers = []
function addDisposer(fn) {
disposers.push(fn)
}
// 範例:註冊事件
const handler = e => console.log(e)
window.addEventListener('resize', handler)
addDisposer(() => window.removeEventListener('resize', handler))
onBeforeUnmount(() => {
disposers.forEach(fn => fn())
})
實際應用場景
圖表套件初始化
- 在
onBeforeMount取得容器尺寸,確保圖表在正確寬高下渲染。 onBeforeUnmount釋放圖表實例,避免記憶體佔用。
- 在
表單自動保存草稿
- 使用
onBeforeUpdate捕捉使用者輸入變化,將內容暫存到localStorage。 - 在
onBeforeUnmount把最後一次草稿寫入持久化儲存,避免遺失。
- 使用
單頁應用 (SPA) 中的路由守護
- 當離開當前頁面前,需要先向後端確認是否有未完成的操作,使用
onBeforeUnmount發送確認請求。
- 當離開當前頁面前,需要先向後端確認是否有未完成的操作,使用
即時通訊 (WebSocket) 連線
- 在
onBeforeMount建立連線,確保在 UI 出現前已經開始接收資料。 onBeforeUnmount關閉連線,釋放資源。
- 在
動態樣式或動畫
onBeforeUpdate可在狀態改變前暫停動畫,等更新完成後再恢復,避免動畫卡頓。
總結
onBeforeMount:元件尚未掛載到真實 DOM 前執行,適合預先取得資料、計算容器尺寸或初始化第三方套件。onBeforeUpdate:在每次渲染前觸發,最適合比對舊新值、暫存 UI 狀態或限制頻繁的副作用。onBeforeUnmount:元件即將被銷毀時呼叫,是清理資源、釋放記憶體的最後防線。
掌握這三個「即將」階段的 Hook,能讓我們在 Vue 3 中寫出更安全、更高效的元件。記得遵守 「只在需要的時機使用」 與 「統一清理」 的原則,讓程式碼保持乾淨、易於維護。祝你在開發 Vue 應用時,玩得開心、寫得順手!