Vue3 Options API(傳統寫法)── 生命周期(beforeCreate、mounted、updated、destroyed)
簡介
在 Vue3 中,即使已經推出了 Composition API,Options API 仍是許多既有專案與新手入門的首選寫法。
Vue 元件的生命周期 (Lifecycle),是指元件從建立、掛載、更新到銷毀的完整過程。掌握每個階段的 Hook,能讓你在正確的時機執行資料初始化、DOM 操作、資源釋放等工作,提升程式的可維護性與效能。
本篇文章將深入探討 beforeCreate、mounted、updated、destroyed 四個最常用的生命周期鉤子,說明它們的執行順序、典型用法,以及在實務開發中應避免的常見陷阱,並提供多個可直接套用的範例,幫助你從「概念」走向「實作」。
核心概念
1. beforeCreate
beforeCreate 是元件實例 建立 後、資料觀測 (reactivity) 與 事件/監聽器 尚未設定前的第一個 Hook。此階段 無法 直接存取 data、computed、props,但可以做以下事情:
- 設定全域變數或外部服務(如 API 客戶端)。
- 初始化非響應式屬性(例如純粹的暫存物件)。
- 注入自訂插件或 mixin。
export default {
name: 'DemoBeforeCreate',
beforeCreate() {
// 只能使用 this.$options 取得元件設定
console.log('Component is being created, options:', this.$options)
// 初始化一個非響應式屬性
this._timer = null // 會在其他 hook 中使用
},
data() {
return {
message: 'Hello Vue!'
}
}
}
Tip:若需要在
data初始化前就取得某些資訊(例如從 localStorage 讀取設定),可在beforeCreate完成,再於data中使用。
2. mounted
mounted 在 虛擬 DOM 渲染完成、真實 DOM 插入 到文件後呼叫。此時 this.$el 已可取得,適合執行:
- DOM 操作(如聚焦、測量尺寸)。
- 第三方 UI 套件初始化(如 Chart.js、Swiper)。
- 發送第一次 API 請求(若不需要在
created中就發送)。
export default {
name: 'DemoMounted',
data() {
return {
users: []
}
},
mounted() {
// 1. 聚焦輸入框
this.$refs.searchInput.focus()
// 2. 取得容器寬度,傳給子元件
const width = this.$el.clientWidth
this.$emit('containerWidth', width)
// 3. 發送 API 請求取得資料
fetch('https://api.example.com/users')
.then(res => res.json())
.then(data => {
this.users = data
})
},
template: `
<div>
<input ref="searchInput" placeholder="搜尋使用者"/>
<ul>
<li v-for="u in users" :key="u.id">{{ u.name }}</li>
</ul>
</div>
`
}
注意:若在
mounted中執行大量計算,可能會阻塞 UI,建議使用requestAnimationFrame或setTimeout延後執行。
3. updated
updated 會在 響應式資料變更、重新渲染完成 後觸發。常見用途:
- 監控特定資料變化(如表格高度改變)。
- 執行副作用(如滾動位置、動畫)。
- 避免無限迴圈:若在
updated中直接改變會觸發再次渲染,必須加條件判斷。
export default {
name: 'DemoUpdated',
data() {
return {
items: [],
page: 1,
loading: false
}
},
methods: {
loadMore() {
if (this.loading) return
this.loading = true
fetch(`https://api.example.com/items?page=${this.page}`)
.then(r => r.json())
.then(res => {
this.items = [...this.items, ...res.items]
this.page++
this.loading = false
})
}
},
updated() {
// 只在 items 增加時自動捲動到底部
this.$nextTick(() => {
const container = this.$refs.list
container.scrollTop = container.scrollHeight
})
},
template: `
<div ref="list" style="height:300px; overflow:auto;">
<div v-for="i in items" :key="i.id">{{ i.title }}</div>
<button @click="loadMore" :disabled="loading">載入更多</button>
</div>
`
}
技巧:使用
this.$nextTick可保證 DOM 已更新,避免取得舊的高度或位置。
4. destroyed(Vue3 改名為 unmounted)
在 Vue3 中,destroyed 被 unmounted 取代,但概念相同:元件即將從 DOM 移除,清理工作 必須在此完成。
- 移除事件監聽(window、document、WebSocket)。
- 銷毀第三方套件實例(如圖表、地圖)。
- 清除計時器或非同步請求。
export default {
name: 'DemoUnmounted',
data() {
return {
timerId: null,
chart: null
}
},
mounted() {
// 設置自動更新的 timer
this.timerId = setInterval(() => {
this.fetchData()
}, 5000)
// 初始化 Chart.js
const ctx = this.$refs.canvas.getContext('2d')
this.chart = new Chart(ctx, {
type: 'line',
data: {/*...*/},
options: {/*...*/}
})
},
methods: {
fetchData() {
// 取得最新資料並更新 chart
// ...
}
},
unmounted() { // Vue3 使用 unmounted
// 清除 timer
clearInterval(this.timerId)
// 銷毀圖表
if (this.chart) {
this.chart.destroy()
}
// 移除全域監聽
window.removeEventListener('resize', this.handleResize)
},
template: `<canvas ref="canvas"></canvas>`
}
提醒:若在
mounted中使用addEventListener,一定要在unmounted中對稱地removeEventListener,避免記憶體洩漏。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
在 beforeCreate 讀取 this.data |
此時資料尚未被觀測,會得到 undefined。 |
只在 created 或之後使用 data。 |
在 mounted 直接改變大量資料 |
會觸發一次以上的重新渲染,降低效能。 | 使用 this.$nextTick 或分批更新。 |
updated 中無條件改變資料 |
會造成 無限迴圈,瀏覽器卡死。 | 加上條件判斷或使用 watch 監聽更精確。 |
忘記在 unmounted 清除計時器 |
計時器仍在背景執行,導致記憶體泄漏。 | 在 unmounted 中 clearInterval/clearTimeout。 |
把 API 請求寫在 mounted |
若元件在路由切換時快速卸載,請求仍在執行。 | 在 created 中發起請求,或在 unmounted 取消 pending 的 Promise (使用 AbortController)。 |
最佳實踐
- 資料取得盡量放在
created,因為此時已完成響應式設定,且不必等到 DOM 完成掛載。 - DOM 操作僅限
mounted,確保this.$el可用。 - 使用
watch取代updated,當只關心特定變數時,watch更具可讀性且不易陷入迴圈。 - 在
unmounted中釋放所有外部資源,包括自訂事件、WebSocket、第三方元件等。 - 避免在生命周期內寫過長的程式,可抽離成方法或自訂 composable(即使仍使用 Options API,也可共用邏輯)。
實際應用場景
1. 表單自動聚焦與驗證
在使用者點擊「新增」按鈕時,彈出表單元件。透過 mounted 聚焦第一個輸入框,beforeCreate 初始化驗證規則,unmounted 清除驗證器。
2. 即時圖表與資料串流
一個儀表板元件在 mounted 建立 WebSocket 連線,收到資料後更新圖表;unmounted 時斷開連線並銷毀圖表實例,避免持續接收已不再顯示的資料。
3. 無限捲動清單
使用 mounted 監聽 scroll 事件,updated 判斷是否已載入新資料並自動捲到最底部,unmounted 移除 scroll 監聽,避免多個清單同時觸發。
4. 動態 SEO 元資訊
在 created 取得頁面資料,mounted 再利用 document.title 或 meta 標籤更新 SEO 資訊,unmounted 時恢復原始標題,確保瀏覽器歷史紀錄正確。
總結
Vue3 的 Options API 仍是許多開發者的首選寫法,而 生命周期 Hook 則是掌控元件行為的關鍵。
beforeCreate用於非響應式的初始化;mounted是操作 真實 DOM、啟動外部套件的最佳時機;updated讓你在資料變更後執行副作用,但要小心避免無限迴圈;unmounted(舊稱destroyed)則負責資源釋放,防止記憶體泄漏。
透過本文的概念說明與實作範例,你應該已能在專案中正確、有效地運用這四個 Hook,並結合 最佳實踐 來提升程式碼的可讀性與效能。未來若需要更彈性的邏輯或跨元件共享,仍可逐步探索 Composition API;但掌握 Options API 的生命周期,仍是 Vue 開發者不可或缺的基礎功。祝你在 Vue3 的旅程中寫出更乾淨、更可靠的程式碼!