本文 AI 產出,尚未審核

Vue3 Composition API(核心) – 生命周期鉤子(onMountedonUnmounted、…)

簡介

在 Vue 3 中,Composition API 讓我們可以把組件的邏輯抽離成可重用的函式(composable),而 生命周期鉤子 則是控制副作用(side‑effect)最關鍵的入口。
正確使用 onMountedonUnmounted 等鉤子,能讓資料抓取、事件註冊、資源釋放等工作在適當的時機執行,避免記憶體泄漏或不一致的 UI。
本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,帶你深入了解 Vue3 中的生命週期鉤子,並提供 實務應用 的指引,適合 初學者到中級開發者

核心概念

1. 生命週期鉤子概覽

鉤子 觸發時機 常見用途
onBeforeMount 組件掛載前 初始化變數、設定 logger
onMounted 組件掛載完成、DOM 已插入 發送 API 請求、註冊第三方插件
onBeforeUpdate 資料變更前 捕捉即將更新的狀態
onUpdated DOM 更新完成 讀取或操作更新後的 DOM
onBeforeUnmount 組件即將卸載 清理計時器、取消訂閱
onUnmounted 組件已卸載 釋放資源、移除全域事件
onErrorCaptured 子組件錯誤冒泡時 捕獲錯誤、上報
onRenderTracked / onRenderTriggered Vue 內部渲染追蹤(較少使用) 性能分析

注意:在 SSR(Server‑Side Rendering) 環境下,只有 onBeforeMountonServerPrefetch 會被呼叫,其他鉤子會被略過。

2. onMounted – 首次渲染完成

import { ref, onMounted } from 'vue'

export default {
  setup() {
    const users = ref([])

    // 只在組件掛載後執行一次
    onMounted(async () => {
      const resp = await fetch('https://api.example.com/users')
      users.value = await resp.json()
    })

    return { users }
  }
}

說明onMounted 內的程式碼會在 DOM 真正插入 後才執行,適合 發送 AJAX初始化圖表 等需要 DOM 的操作。

3. onUnmounted – 清理工作

import { ref, onMounted, onUnmounted } from 'vue'

export default {
  setup() {
    const timer = ref(null)

    onMounted(() => {
      timer.value = setInterval(() => {
        console.log('tick')
      }, 1000)
    })

    // 組件卸載時必須清除計時器,避免 memory leak
    onUnmounted(() => {
      clearInterval(timer.value)
    })
  }
}

說明onUnmounted資源釋放 的絕佳時機,務必把所有在 onMounted 中建立的外部連結(事件、訂閱、計時器)都在此清除。

4. 其他常用鉤子

onBeforeMount

import { onBeforeMount } from 'vue'

onBeforeMount(() => {
  console.log('Component will mount soon')
})

onUpdated – DOM 更新後取得最新尺寸

import { ref, onMounted, onUpdated } from 'vue'

export default {
  setup() {
    const box = ref(null)
    const size = ref({ width: 0, height: 0 })

    const measure = () => {
      if (box.value) {
        size.value = {
          width: box.value.offsetWidth,
          height: box.value.offsetHeight
        }
      }
    }

    onMounted(measure)
    onUpdated(measure)   // 每次資料變動後重新測量

    return { box, size }
  },
  template: `
    <div ref="box" class="my-box">
      <p>寬度:{{ size.width }}px, 高度:{{ size.height }}px</p>
    </div>
  `
}

onErrorCaptured – 捕捉子組件錯誤

import { onErrorCaptured } from 'vue'

onErrorCaptured((err, instance, info) => {
  console.error('子組件錯誤:', err, info)
  // 返回 false 可阻止錯誤繼續向上冒泡
  return false
})

5. 結合 watchEffect 與生命週期

import { ref, watchEffect, onMounted, onUnmounted } from 'vue'

export default {
  setup() {
    const count = ref(0)

    // watchEffect 內部會自動在組件卸載時停止
    const stop = watchEffect(() => {
      console.log('count 改變為', count.value)
    })

    onMounted(() => {
      const id = setInterval(() => count.value++, 1000)
      // 以 onUnmounted 方式手動清除
      onUnmounted(() => clearInterval(id))
    })
  }
}

小技巧watchEffect 本身會在組件卸載時自動停止,但若裡面建立了外部資源(如 setInterval),仍需在 onUnmounted 手動清除。

常見陷阱與最佳實踐

陷阱 可能的問題 解決方案 / 最佳實踐
忘記在 onUnmounted 清除副作用 記憶體泄漏、重複事件觸發 始終配對 onMountedonUnmounted,使用 try…finallyreturn 清理
onMounted 中直接使用 await,導致組件渲染阻塞 UI 卡頓、使用者體驗差 await 包在 非阻塞 的函式裡,或使用 Promise.then,同時顯示 loading 狀態
在 SSR 環境使用只能在瀏覽器執行的 API(如 windowdocument 產生 ReferenceError 使用 onMounted 包裹,或檢查 import.meta.env.SSR
多次呼叫同一鉤子,導致副作用重複 重複註冊事件、重複資料請求 集中管理:將副作用抽離成 composable,確保只在需要時呼叫一次
onErrorCaptured 中未返回 false 錯誤會繼續向上冒泡,可能導致全局錯誤處理被觸發 明確回傳 false阻止冒泡,或自行上報錯誤

最佳實踐小結

  1. 副作用與清理必配對onMountedonUnmountedwatchEffectstop
  2. 保持 composable 純粹:把 API 請求、WebSocket、圖表初始化等封裝成獨立函式,讓 setup 只負責呼叫。
  3. 使用 try…catch 包住 async 操作,確保錯誤不會中斷生命週期流程。
  4. 在 SSR 時檢查環境,避免使用只能在客戶端執行的 API。
  5. 適時使用 onBeforeUnmount:在某些情況下(如需要先做最後一次資料同步)可先於 onUnmounted 執行。

實際應用場景

A. 整合第三方圖表庫(Chart.js)

import { ref, onMounted, onUnmounted } from 'vue'
import Chart from 'chart.js/auto'

export default {
  setup() {
    const canvasRef = ref(null)
    let chartInstance = null

    onMounted(() => {
      chartInstance = new Chart(canvasRef.value, {
        type: 'line',
        data: { /* ... */ },
        options: { responsive: true }
      })
    })

    onUnmounted(() => {
      if (chartInstance) chartInstance.destroy()
    })

    return { canvasRef }
  },
  template: `<canvas ref="canvasRef"></canvas>`
}

重點:圖表必須在 onMounted 取得 <canvas>,而在 onUnmounted 釋放資源,防止記憶體泄漏。

B. WebSocket 即時通訊

import { ref, onMounted, onUnmounted } from 'vue'

export default {
  setup() {
    const messages = ref([])
    let ws = null

    onMounted(() => {
      ws = new WebSocket('wss://example.com/chat')
      ws.addEventListener('message', e => {
        messages.value.push(e.data)
      })
    })

    onUnmounted(() => {
      ws && ws.close()
    })

    return { messages }
  }
}

C. 表單自動保存(debounce)

import { ref, watch, onMounted, onUnmounted } from 'vue'
import { debounce } from 'lodash-es'

export default {
  setup() {
    const form = ref({ name: '', email: '' })

    const save = debounce(() => {
      console.log('自動保存資料', form.value)
      // 呼叫 API...
    }, 800)

    const stopWatch = watch(form, save, { deep: true })

    onUnmounted(() => {
      stopWatch()   // 停止 watch
      save.cancel() // 取消 pending 的 debounce
    })

    return { form }
  }
}

總結

Vue 3 的 Composition API 讓我們以函式的方式管理生命週期,onMountedonUnmounted 等鉤子是控制副作用的核心。

  • 正確的 配對清理 能避免記憶體泄漏與意外行為。
  • 透過 抽離 composable,可讓程式碼更易維護、可重用。
  • 注意 SSR 環境 以及 錯誤捕獲 的細節,能提升應用的穩定性。

掌握這些概念與實作範例,你就能在 Vue3 專案中自如地處理資料抓取、第三方套件整合、即時通訊等需求,寫出 乾淨、可維護、效能友好 的程式碼。祝開發順利!