本文 AI 產出,尚未審核

Vue3 錯誤處理與除錯:Vue.config.errorHandler 完全指南


簡介

在開發大型 Vue3 應用時,錯誤的即時捕捉與統一處理 是提升使用者體驗與維護效率的關鍵。若不加以控制,任何未處理的例外都會直接導致整個組件樹崩潰,使用者會看到空白畫面或瀏覽器的錯誤訊息,這不僅影響產品形象,也讓除錯變得更困難。

Vue3 為了提供全局的錯誤攔截機制,保留了 Vue.config.errorHandler(在 Vue 2 時同樣存在)以及在組件內部的 errorCaptured 鉤子。透過 全局錯誤處理器,開發者可以統一記錄、上報錯誤,甚至在錯誤發生時顯示友善的 UI。本文將深入說明 Vue.config.errorHandler 的使用方式、常見陷阱與最佳實踐,並提供多個實作範例,讓你能在實務專案中立即上手。


核心概念

1. 為什麼需要全局錯誤處理器?

  • 統一入口:所有在渲染過程、生命週期鉤子、事件處理器、watcher 中拋出的錯誤,都會被 Vue 捕捉並傳遞給 errorHandler
  • 集中上報:只需要在一個地方加入上報程式碼(例如 Sentry、LogRocket),即可把所有錯誤傳到後端。
  • 使用者體驗:可以在錯誤發生時顯示「系統暫時無法使用」的提示頁面,避免白屏。

2. Vue.config.errorHandler 的簽名

Vue.config.errorHandler = (err, vm, info) => {
  // err   : Error 物件 (包含 stack、message 等)
  // vm    : 發生錯誤的 Vue 元件實例 (null 表示在根實例之外)
  // info  : 錯誤發生的上下文字串,例如 "render function"、"watcher"
}
  • err:實際的例外物件,可直接 console.error(err) 或傳給上報服務。
  • vm:錯誤所在的組件實例,透過 vm.$options.namevm.$router 可取得更多資訊。
  • info:Vue 提供的錯誤類型說明,對於定位問題很有幫助。

注意:在 Vue3 中,我們仍然使用 app.config.errorHandlerappcreateApp 回傳的實例),而非全域的 Vue.config。本文的範例會同時示範兩種寫法,以兼容 Vue2 與 Vue3。

3. 設定方式

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

// Vue3 推薦寫法
app.config.errorHandler = (err, vm, info) => {
  // 這裡寫入錯誤處理邏輯
  console.error('[全局錯誤]', err, info, vm)
}

// 若你仍在使用 Vue2,寫法如下(僅供參考)
// Vue.config.errorHandler = (err, vm, info) => { ... }

app.mount('#app')

4. 與 errorCaptured 的關係

  • errorCaptured組件層級的錯誤捕獲鉤子,只會攔截子組件拋出的錯誤,且可自行決定是否向上冒泡(回傳 false 會阻止冒泡)。
  • errorHandler 則是全局的最後一道防線,所有未被 errorCaptured 捕獲的錯誤最終會到達此處。

程式碼範例

以下示範 5 個實用範例,涵蓋從最簡單的全局錯誤紀錄到結合第三方上報服務、條件過濾與自訂錯誤 UI。

範例 1:最基礎的全局錯誤日誌

// main.js
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.config.errorHandler = (err, vm, info) => {
  // 直接印出錯誤資訊,適合開發階段快速除錯
  console.error('[Vue Error]', err)
  console.warn('發生位置:', info)
  if (vm) {
    console.warn('錯誤來源元件:', vm.$options.name || vm.$.type.name)
  }
}

app.mount('#app')

說明:此範例僅在開發環境使用,讓開發者能即時在 console 中看到完整錯誤堆疊。

範例 2:結合 Sentry 的全局上報

// main.js
import { createApp } from 'vue'
import * as Sentry from '@sentry/vue'
import { Integrations } from '@sentry/tracing'
import App from './App.vue'

const app = createApp(App)

// 初始化 Sentry
Sentry.init({
  app,
  dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
  integrations: [new Integrations.BrowserTracing()],
  tracesSampleRate: 1.0,
})

// 全局錯誤處理器,同時把錯誤送給 Sentry
app.config.errorHandler = (err, vm, info) => {
  // Sentry 已自動捕獲 Vue 錯誤,這裡僅做自訂資訊補足
  Sentry.captureException(err, {
    extra: {
      component: vm?.$options?.name,
      info,
    },
  })
  // 同時保留 console 輸出,方便本機除錯
  console.error('[Sentry]', err)
}
app.mount('#app')

說明:Sentry 內部已註冊自己的錯誤捕獲器,errorHandler 只負責補充自訂欄位(如元件名稱)。

範例 3:條件過濾 – 只上報 production 環境

// utils/errorHandler.js
export function installGlobalErrorHandler(app) {
  const isProd = import.meta.env.PROD

  app.config.errorHandler = (err, vm, info) => {
    // 1. 本機開發階段只打印
    if (!isProd) {
      console.error('[開發模式]', err)
      return
    }

    // 2. Production:上傳至自家後端 API
    fetch('/api/report-error', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        message: err.message,
        stack: err.stack,
        component: vm?.$options?.name,
        info,
        url: location.href,
        userAgent: navigator.userAgent,
        timestamp: Date.now(),
      }),
    }).catch(() => {
      // 若上報失敗,仍保留 console
      console.error('Error reporting failed', err)
    })
  }
}
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import { installGlobalErrorHandler } from '@/utils/errorHandler'

const app = createApp(App)
installGlobalErrorHandler(app)
app.mount('#app')

說明:在開發階段不會向伺服器發送過多訊息,避免噪音;上線後才正式上報。

範例 4:自訂錯誤 UI – 全局錯誤佈景

<!-- ErrorOverlay.vue -->
<template>
  <div v-if="visible" class="error-overlay">
    <h2>系統發生錯誤</h2>
    <p>我們已自動記錄此錯誤,請稍後再試。</p>
    <button @click="reload">重新整理</button>
  </div>
</template>

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

const visible = ref(false)

export function showErrorOverlay() {
  visible.value = true
}

function reload() {
  location.reload()
}
</script>

<style scoped>
.error-overlay {
  position: fixed;
  inset: 0;
  background: rgba(255, 0, 0, 0.1);
  color: #c00;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 9999;
}
</style>
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import ErrorOverlay, { showErrorOverlay } from '@/components/ErrorOverlay.vue'

const app = createApp(App)

// 全局錯誤處理器:顯示錯誤覆蓋層
app.config.errorHandler = (err, vm, info) => {
  console.error('[全局錯誤]', err)
  // 只在 production 顯示 UI
  if (import.meta.env.PROD) {
    showErrorOverlay()
  }
}

// 把 ErrorOverlay 注入根組件
app.component('ErrorOverlay', ErrorOverlay)

app.mount('#app')

說明:當錯誤發生時,showErrorOverlay() 會讓覆蓋層顯示,使用者不會直接看到白屏。

範例 5:結合 errorCaptured 進行局部過濾

// MyButton.vue
<template>
  <button @click="triggerError">點我會錯誤</button>
</template>

<script>
export default {
  name: 'MyButton',
  methods: {
    triggerError() {
      // 故意拋出錯誤測試
      throw new Error('按鈕內部錯誤')
    },
  },
  // 只捕獲此組件的錯誤,避免向全局傳遞
  errorCaptured(err, vm, info) {
    console.warn('[MyButton 捕獲]', err.message)
    // 回傳 false,阻止冒泡到全局 errorHandler
    return false
  },
}
</script>

說明errorCaptured 讓開發者在特定組件中自行處理錯誤,若回傳 false,則不會再進入全局 errorHandler,適合不希望上報的預期錯誤(如使用者輸入錯誤導致的驗證失敗)。


常見陷阱與最佳實踐

陷阱 說明 最佳實踐
忘記在 Vue3 使用 app.config.errorHandler 仍寫 Vue.config.errorHandler 會在 Vue3 中失效。 createApp 後立即設定 app.config.errorHandler
errorHandler 內部再次拋出錯誤 若處理程式本身出錯,會形成無限迴圈或失去錯誤資訊。 使用 try…catch 包裹上報程式碼,確保不會再拋出未捕獲例外。
上報過於頻繁 大量錯誤同時上報會造成伺服器壅塞。 加入節流或批次上報(如每 5 筆一次或使用 debounce)。
errorHandler 中直接操作 UI 若同時觸發多個錯誤,可能導致 UI 狀態不一致。 僅負責紀錄/上報,UI 顯示交給單獨的狀態管理(如 Pinia)或全局事件
忽略 info 參數 info 能快速定位錯誤發生於 render、watcher…,不使用會失去關鍵線索。 在日誌或上報時加入 info,方便日後排查。

最佳實踐總結

  1. 在主入口(main.js)即完成全局錯誤處理器的安裝,確保所有組件在掛載前已被攔截。
  2. 區分開發與正式環境:開發時直接 console.error,正式環境才上報。
  3. 結合第三方服務(Sentry、LogRocket)或自建 API,統一管理錯誤。
  4. 保留 errorCaptured 用於局部過濾,避免非預期錯誤被過度上報。
  5. 將錯誤資訊傳遞給狀態管理(Pinia、Vuex)或全局事件,讓 UI 只負責呈現。

實際應用場景

1. 電商平台的結帳流程

在結帳頁面,任何渲染錯誤都可能導致使用者無法完成付款。透過 errorHandler 捕捉錯誤後,立即上報至監控系統,同時顯示友善的錯誤覆蓋層(範例 4),讓使用者知道系統暫時不可用,並提供「重新整理」或「聯絡客服」的選項。

2. SaaS 後台管理系統

管理員操作多個互相嵌套的圖表與表格,個別組件的錯誤不應影響整體儀表板。此時在每個圖表組件內使用 errorCaptured(範例 5)自行捕獲,僅在必要時(如資料解析錯誤)向全局上報,避免噪音過多。

3. 行動端 PWA

PWA 需要在離線或網路不穩時仍能提供基本功能。全局錯誤處理器可以偵測網路相關的例外,自動切換到離線模式或顯示「網路不穩」的提示,提升使用者留存率。


總結

Vue.config.errorHandler(Vue3 中的 app.config.errorHandler)是 Vue 全局錯誤攔截的核心,配合 errorCaptured、第三方上報服務與自訂 UI,能夠讓開發者在 開發、測試與上線 三個階段都保持良好的錯誤可視化與可控性。

  • 設定位置:在 createApp 後立即設定,確保所有組件在掛載前已被攔截。
  • 開發 vs Production:開發階段直接 console.error,上線後才上報並顯示友善 UI。
  • 避免過度上報:加入環境檢查、節流與錯誤分類。
  • 局部過濾:使用 errorCaptured 防止不必要的全局上報。
  • 實務應用:電商結帳、SaaS 後台、PWA 離線體驗等,都能從全局錯誤處理中受益。

將這些概念與範例內化後,你的 Vue3 專案將不再因為未捕獲的例外而「白屏」,而是以 可觀測、可維護、可回復 的方式持續提供穩定服務。祝開發順利,錯誤遠離!