本文 AI 產出,尚未審核

Vue3 錯誤處理與除錯 – onErrorCaptured 完全指南


簡介

在大型 Vue3 應用程式中,錯誤不僅會影響使用者體驗,也可能導致資料遺失或功能中斷。因此,掌握 Vue 內建的錯誤捕獲機制是每位前端開發者的必備功課。
onErrorCaptured 是 Vue3 中提供的 組件層級錯誤攔截 API,允許我們在子組件拋出錯誤時,於父層或祖層組件先行處理、記錄或轉譯錯誤,再決定是否讓錯誤繼續向上冒泡。

本篇文章將從概念、實作、常見陷阱到最佳實踐,一步步帶你在 Vue 3 Composition API 中善用 onErrorCaptured,打造更穩定、易除錯的應用。


核心概念

1. 為什麼需要 onErrorCaptured

  • 錯誤範圍控制:只捕獲特定子樹的錯誤,而不影響全局錯誤處理 (app.config.errorHandler)。
  • 錯誤轉譯:將技術性錯誤轉換成使用者友善的訊息或 UI。
  • 資源釋放:在錯誤發生時,執行清理工作(例如取消訂閱、釋放計時器)。

注意onErrorCaptured 只能在 setup組件選項 API 中使用,且只能在 渲染期間 拋出的錯誤被捕獲(例如 rendersetupwatchEffect)。

2. 基本語法

import { onErrorCaptured } from 'vue'

export default {
  setup() {
    // 捕獲子組件錯誤
    onErrorCaptured((err, instance, info) => {
      console.error('子組件錯誤:', err)
      // 回傳 true 可阻止錯誤繼續向上冒泡
      return false   // 讓錯誤繼續冒泡到更上層或全局處理器
    })
  }
}
  • 參數說明

    • err:實際的 Error 物件。
    • instance:拋出錯誤的子組件實例 (ComponentPublicInstance)。
    • info:錯誤發生的上下文字串("render""setup""watchEffect" 等)。
  • 回傳值

    • false(預設) → 錯誤會繼續向上冒泡。
    • true → 錯誤被阻止,不會傳遞到更上層或全局錯誤處理器。

3. 與全局錯誤處理 (app.config.errorHandler) 的關係

層級 觸發時機 是否會觸發下一層
setup/render 中的 onErrorCaptured 子組件錯誤 取決於回傳值
app.config.errorHandler 尚未被 onErrorCaptured 阻止的錯誤 只會跑一次

實務建議:先在局部使用 onErrorCaptured 針對特定功能做細部處理,再把未處理的錯誤交給全局 errorHandler 做最後的紀錄或上報。

4. 常見使用情境

情境 為什麼要用 onErrorCaptured
表單驗證失敗的非同步請求 可在父層顯示統一的錯誤提示
第三方 UI 元件拋出渲染錯誤 父層可回退到備用 UI,避免整頁崩潰
多層嵌套的路由視圖 在根路由捕獲子路由錯誤,統一導向錯誤頁面

程式碼範例

下面提供 五個實用範例,從最簡單的錯誤捕獲到進階的錯誤轉譯與資源清理。

範例 1️⃣:最基本的錯誤捕獲

// Parent.vue
<template>
  <Child />
</template>

<script setup>
import { onErrorCaptured } from 'vue'
import Child from './Child.vue'

onErrorCaptured((err, instance, info) => {
  console.warn('捕獲到子組件錯誤:', err.message)
  // 不阻止冒泡,讓全局 errorHandler 仍能收到
  return false
})
</script>
// Child.vue
<template>
  <div>{{ data.text }}</div>
</template>

<script setup>
import { ref } from 'vue'

const data = ref(null)

// 故意在 render 時拋出錯誤
if (!data.value) {
  throw new Error('data 尚未初始化')
}
</script>

說明Parent.vue 透過 onErrorCaptured 監聽子組件 Child.vue 的渲染錯誤,並把錯誤資訊寫入 console。


範例 2️⃣:阻止錯誤向上冒泡

// Dashboard.vue
<template>
  <ErrorProneComponent />
  <p v-if="errorMsg" class="error">{{ errorMsg }}</p>
</template>

<script setup>
import { ref, onErrorCaptured } from 'vue'
import ErrorProneComponent from './ErrorProneComponent.vue'

const errorMsg = ref('')

onErrorCaptured((err) => {
  errorMsg.value = `發生錯誤:${err.message}`
  // 返回 true 阻止錯誤繼續冒泡
  return true
})
</script>

說明:當 ErrorProneComponent 拋出錯誤時,Dashboard.vue 直接在畫面上顯示錯誤訊息,且 不讓錯誤傳到全局,避免整個應用程式崩潰。


範例 3️⃣:錯誤轉譯成使用者友善訊息

// ApiFetcher.vue
<template>
  <button @click="load">載入資料</button>
  <div v-if="data">{{ data }}</div>
</template>

<script setup>
import { ref, onErrorCaptured } from 'vue'

const data = ref(null)

async function load() {
  // 假設此 API 會拋出 network error
  const res = await fetch('https://api.example.com/data')
  if (!res.ok) throw new Error('Network response was not ok')
  data.value = await res.json()
}

// 捕獲非同步錯誤
onErrorCaptured((err) => {
  // 只處理我們關心的錯誤類型
  if (err.message.includes('Network')) {
    alert('無法連線至伺服器,請稍後再試。')
    return true   // 阻止錯誤冒泡
  }
  return false
})
</script>

說明onErrorCaptured 會捕獲 load() 中的非同步錯誤,轉成 彈窗提醒,同時阻止錯誤繼續傳遞。


範例 4️⃣:在錯誤發生時清理資源

// TimerComponent.vue
<template>
  <p>倒數:{{ count }}</p>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, onErrorCaptured } from 'vue'

const count = ref(10)
let timerId = null

onMounted(() => {
  timerId = setInterval(() => {
    count.value--
    if (count.value === 5) {
      // 故意拋出錯誤,模擬計時過程中的例外
      throw new Error('計時器意外錯誤')
    }
  }, 1000)
})

// 捕獲錯誤後清除計時器
onErrorCaptured((err) => {
  clearInterval(timerId)
  console.error('計時器已被清除:', err.message)
  // 仍讓錯誤冒泡,以便全局紀錄
  return false
})

onBeforeUnmount(() => clearInterval(timerId))
</script>

說明:當倒數計時中拋出錯誤,onErrorCaptured 會立即 清除計時器,避免資源泄漏,同時把錯誤交給全局處理。


範例 5️⃣:在路由守衛中結合 onErrorCaptured

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
import NotFound from '@/views/NotFound.vue'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: Home },
    { path: '/:catchAll(.*)', component: NotFound }
  ]
})

// 全局路由錯誤處理
router.onError((error) => {
  console.error('路由錯誤:', error)
})

export default router
// App.vue
<template>
  <router-view />
</template>

<script setup>
import { onErrorCaptured } from 'vue'
import router from './router'

onErrorCaptured((err, instance, info) => {
  // 若是路由懶載入失敗,導向 404
  if (err.message.includes('Failed to fetch dynamically imported module')) {
    router.replace('/404')
    return true   // 阻止錯誤冒泡
  }
  return false
})
</script>

說明:當懶載入的路由組件失敗(例如網路斷線),App.vue 會捕獲錯誤並 自動導向 404,提升使用者體驗。


常見陷阱與最佳實踐

陷阱 說明 解決方式
只在 setup 中使用 在普通的 methodscomputed 中呼叫 onErrorCaptured 不會生效。 確保把 onErrorCaptured 放在 setup() 內或使用 Options API 的 errorCaptured 鉤子。
忘記回傳布林值 預設回傳 undefined 會被視為 false,錯誤會繼續冒泡。 明確 return true(阻止)或 return false(允許)。
捕獲非渲染錯誤 onErrorCaptured 只會捕獲 渲染階段rendersetupwatchEffect)的錯誤,無法捕獲 async/await 之外的 Promise 錯誤。 針對 Promise 使用 try/catch 或全局 app.config.errorHandler
過度阻止錯誤 把所有錯誤都 return true 會隱藏真正的程式錯誤,造成除錯困難。 僅對可預期的錯誤阻止,其他錯誤仍交給全局處理或開發者工具。
在同一層多次註冊 多次呼叫 onErrorCaptured 會產生多個攔截器,順序不易控制。 盡量在同一個 setup 中集中管理,或使用自訂 composable 包裝。

最佳實踐清單

  1. 先局部、後全局:先用 onErrorCaptured 處理特定子樹的錯誤,再讓未處理的錯誤交給 app.config.errorHandler
  2. 只阻止可預期錯誤:例如 API 失敗、使用者輸入錯誤;對程式邏輯錯誤仍保持冒泡。
  3. 加入錯誤上報:在捕獲函式內呼叫自訂的 logError(error),將錯誤送至 Sentry、LogRocket 等服務。
  4. 結合 TypeScript:使用 Error 的自訂型別(interface ApiError extends Error { code: number })提升錯誤辨識度。
  5. 保持 UI 回饋:捕獲錯誤後即時更新 UI(toast、dialog、inline message),避免使用者感到「卡住」或「無回應」。

實際應用場景

1. 表單提交失敗的集中處理

在大型表單中,每個欄位可能都有獨立的驗證元件。透過在表單父層使用 onErrorCaptured,可以 一次捕獲所有子元件的驗證例外,並顯示統一的錯誤訊息列。

2. 第三方圖表庫渲染錯誤

使用如 EChartsChart.js 時,若資料格式不正確會導致渲染階段拋錯。將圖表容器包在一個 ChartWrapper,在外層使用 onErrorCaptured 捕獲渲染錯誤,自動切換顯示「暫無資料」,避免整頁崩潰。

3. 多層路由懶載入失敗

在 SPA 中,常透過 defineAsyncComponent 懶載入路由組件。若網路斷線或資源不存在,會拋出錯誤。透過根組件的 onErrorCaptured,可以 偵測到此類錯誤並導向自訂的 404/500 頁面

4. 跨域請求的統一錯誤 UI

在微前端架構裡,各子應用可能自行發送跨域請求。將所有子應用包在一個 MicroAppContainer,使用 onErrorCaptured 捕獲子應用的網路錯誤,統一顯示「服務暫時不可用」,提升整體使用者體驗。


總結

  • onErrorCaptured 是 Vue3 組件層級的錯誤攔截 API,讓我們在子樹錯誤發生時即時處理、轉譯或清理資源。
  • 正確使用 回傳布林值限制捕獲範圍,可以避免錯誤被過度隱藏,同時保持除錯的透明度。
  • 結合全局 app.config.errorHandler、路由守衛或自訂上報服務,能形成 多層次、彈性且可維護 的錯誤處理機制。

透過本文的概念說明與實作範例,你應該已經能在日常開發中熟練使用 onErrorCaptured,讓 Vue3 應用在面對不可預期的例外時,仍能保持 穩定、友善且易於除錯。祝開發順利! 🚀