本文 AI 產出,尚未審核

Vue 3 生命周期對應表(Options API vs Composition API)

簡介

在 Vue 3 中,生命週期(Lifecycle) 是每個元件從建立、渲染、更新到銷毀的完整過程。了解生命週期不僅能讓我們在正確的時機執行初始化或清除工作,還能避免資源洩漏、效能瓶頸等常見問題。
Vue 3 同時支援 Options APIComposition 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)必須使用 refreactive 包裝,才能保持響應式。
  • 生命週期函式直接在 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>

重點onMountedonUnmounted 直接在 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 清理訂閱 記憶體洩漏、事件觸發錯誤 所有 watcheventListenersetIntervalfetch 的 abort controller 必須在 onUnmounted 中解除
created 中使用 this.$el this.$elnull,因為尚未掛載 必須改為 mountedonMounted
混用 Options 與 Composition,導致重複生命週期 同一階段的程式碼執行兩次 在同一元件內盡量選擇單一 API,或確保兩者的邏輯不衝突

最佳實踐

  1. 盡量在 setup 中完成初始化:除非真的需要存取 DOM,否則把資料、計算屬性、watcher 放在 setup,減少 created/mounted 的使用。
  2. 使用 composable 抽離共用邏輯:讓生命週期與業務程式碼分離,提升可測試性與可維護性。
  3. onUnmounted 中清理所有副作用:包括 watchEffect、第三方庫的事件、定時器等。
  4. 使用 TypeScript 時,為 onXxx 加上明確的型別:可避免因錯誤回傳值(如 return false)而產生的行為差異。

實際應用場景

場景 為何需要生命週期 建議寫法
首次載入即發送 API 必須在 DOM 完全掛載前取得資料,避免 UI 閃爍 onMounted(Composition)或 mounted(Options)
根據路由參數動態載入資料 路由變更時需重新請求 watch 搭配 onMounted,或 onBeforeRouteUpdate
使用第三方 UI 元件(如 Chart.js) 必須在 <canvas> 元素掛載後才能初始化圖表 onMounted 初始化圖表,onUnmounted 銷毀圖表實例
全局錯誤監控 捕捉子元件的異常,統一上報 在根元件使用 onErrorCaptured
動畫過渡 在更新前後執行 CSS/JS 動畫 onBeforeUpdate / onUpdatedonBeforeMount / 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 APIComposition API 之間是一一對應的,只是呼叫方式不同。
  • 透過對應表,我們可以快速將舊有 Options 寫法遷移至 Composition,或在新專案中直接使用 setup + onXxx
  • 正確使用生命週期能讓 初始化、清理、錯誤處理 等工作更有條理,同時避免資源洩漏與不必要的重渲染。
  • 把重複的生命週期邏輯抽成 composable,不僅提升程式碼可讀性,也方便單元測試與團隊協作。

掌握這張對應表與最佳實踐,你就能在 Vue 3 中自在切換兩種 API,寫出結構清晰、效能優化且易於維護的元件!祝開發順利 🚀