本文 AI 產出,尚未審核

Vue3 – 錯誤處理與除錯:全域錯誤邏輯

簡介

在單頁應用程式 (SPA) 中,錯誤不會像傳統多頁網站那樣直接跳出瀏覽器的錯誤頁面,而是會在 JavaScript 執行階段靜默發生。若沒有適當的錯誤捕捉與回報機制,使用者可能會看到「畫面卡住」或「功能失效」的情況,開發團隊也難以追蹤問題根源。
全域錯誤處理(Global Error Handling)正是為了在 Vue3 應用的最外層集中捕捉未處理的例外、Promise 錯誤或生命週期錯誤,進而統一記錄、顯示友善訊息,甚至自動上報給後端監控系統。

本篇文章將帶你從概念到實作,完整掌握 Vue3 中的全域錯誤邏輯,讓你的應用更穩定、更易除錯。


核心概念

1. Vue3 全域錯誤捕捉 API

Vue3 提供了兩個主要的全域錯誤捕捉鉤子:

API 說明 觸發時機
app.config.errorHandler 捕捉 同步 錯誤(如 rendersetup、生命週期) 任何 component 內部未被 try/catch 包住的例外
app.config.warnHandler 捕捉 Vue 警告(如 prop type 不符) Vue 在開發環境發出警告時

這兩個設定在應用程式啟動時一次性註冊,之後所有子組件都會自動受惠。

2. 全域 Promise 錯誤(未捕捉的 rejected)

瀏覽器提供 window.onunhandledrejection 事件,可捕捉未被 .catch() 處理的 Promise 錯誤。將它與 Vue 的 errorHandler 結合,可做到 全域錯誤一網打盡

3. 錯誤資訊的結構化

為了讓錯誤上報更有價值,建議將錯誤資訊整理為以下格式:

interface GlobalErrorPayload {
  message: string;          // 錯誤訊息
  stack?: string;           // 堆疊追蹤(若有)
  component?: string;       // 發生錯誤的 component 名稱
  propsData?: Record<string, any>; // 當時傳入的 props
  url: string;              // 當前路由
  timestamp: number;        // 發生時間
}

4. 錯誤回報策略

  • 本地顯示:使用者可見的 toast / dialog,提供友善提示。
  • 遠端上報:將錯誤 payload POST 給錯誤監控服務(如 Sentry、BugSnag、或自行建置的 API)。
  • 本地儲存:在無網路時先寫入 IndexedDB,待網路恢復再上傳。

程式碼範例

下面的範例展示了從 建立 Vue 應用註冊全域錯誤處理、到 整合 Promise 錯誤與遠端上報 的完整流程。每段程式碼皆附上說明註解。

1️⃣ 建立 Vue3 應用與全域 errorHandler

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { reportError } from './utils/errorReporter' // 之後會實作

const app = createApp(App)

// ★ 設定全域 errorHandler
app.config.errorHandler = (err, vm, info) => {
  // 1. 取得錯誤基本資訊
  const payload = {
    message: err.message,
    stack: err.stack,
    component: vm?.$options?.name || 'anonymous',
    propsData: vm?.$props,
    url: router.currentRoute.value.fullPath,
    timestamp: Date.now(),
    info, // Vue 提供的錯誤類型描述
  }

  // 2. 顯示友善訊息(使用 UI 框架的 toast)
  vm?.$toast?.error('發生未預期的錯誤,已自動回報給開發團隊')

  // 3. 上報至遠端(非同步,不阻塞 UI)
  reportError(payload).catch(console.error) // 防止上報失敗拋出二次錯誤
}

// ★ 設定全域 warnHandler(可選)
app.config.warnHandler = (msg, vm, trace) => {
  console.warn(`[Vue warn] ${msg}\nTrace: ${trace}`)
}

app.use(router).mount('#app')

重點errorHandler 內部絕不應拋出錯誤,否則會形成遞迴。若上報失敗,只需要在 catch 中記錄即可。

2️⃣ 捕捉未處理的 Promise 錯誤

// errorGlobal.js(可在 main.js 之前 import)
window.addEventListener('unhandledrejection', event => {
  const err = event.reason instanceof Error ? event.reason : new Error(String(event.reason))

  const payload = {
    message: err.message,
    stack: err.stack,
    component: 'global-promise',
    url: location.href,
    timestamp: Date.now(),
    info: 'unhandledrejection',
  }

  // 同樣使用 reportError,上報遠端
  reportError(payload).catch(console.error)

  // 防止瀏覽器默認的 console 警告(可依需求保留)
  event.preventDefault()
})

提示:在開發環境可以先 console.error(err) 觀察,再決定是否上報。

3️⃣ 建立錯誤上報工具(簡易版)

// utils/errorReporter.js
export async function reportError(payload) {
  // 若是離線狀態,先寫入 localForage(IndexedDB 包裝)
  if (!navigator.onLine) {
    const { setItem } = await import('localforage')
    const queue = (await setItem('errorQueue', [])) || []
    queue.push(payload)
    await setItem('errorQueue', queue)
    return
  }

  // 正式上報
  const response = await fetch('/api/error-report', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  })

  if (!response.ok) throw new Error('Error report failed')
}

說明:此範例示範了 離線緩衝JSON 序列化錯誤回傳檢查,讓上報更具彈性。

4️⃣ 在組件內使用 try/catch + 手動上報(補強)

<template>
  <button @click="loadData">載入資料</button>
</template>

<script setup>
import { ref } from 'vue'
import { reportError } from '@/utils/errorReporter'

const data = ref(null)

async function loadData() {
  try {
    const res = await fetch('/api/data')
    if (!res.ok) throw new Error('Network response was not ok')
    data.value = await res.json()
  } catch (err) {
    // 手動上報,讓錯誤訊息更具上下文
    await reportError({
      message: err.message,
      stack: err.stack,
      component: 'DataFetcher',
      url: location.href,
      timestamp: Date.now(),
      info: 'loadData() failed',
    })
    // 顯示 UI 提示
    alert('資料載入失敗,請稍後再試')
  }
}
</script>

技巧:即使已設定全域 errorHandler,在關鍵流程(如 API 呼叫)仍建議自行捕捉,加入業務層面的描述info 欄位),方便日後排查。

5️⃣ 整合 Sentry(第三方監控)作為遠端上報

// plugins/sentry.js
import * as Sentry from '@sentry/vue'
import { BrowserTracing } from '@sentry/tracing'
import { createApp } from 'vue'

export function initSentry(app) {
  Sentry.init({
    app,
    dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
    integrations: [new BrowserTracing()],
    // 設定自訂的全域錯誤處理,讓 Sentry 捕捉到 Vue 的 errorHandler
    vueOptions: {
      errorHandler(err, vm, info) {
        // 讓 Sentry 先收集
        Sentry.captureException(err)

        // 再交給我們自己的 handler(如上面的 app.config.errorHandler)
        // 這裡可以直接呼叫 app.config.errorHandler
        if (app.config.errorHandler) {
          app.config.errorHandler(err, vm, info)
        }
      },
    },
    tracesSampleRate: 1.0,
  })
}

// main.js 中使用
import { createApp } from 'vue'
import App from './App.vue'
import { initSentry } from '@/plugins/sentry'

const app = createApp(App)
initSentry(app) // 先掛載 Sentry
app.mount('#app')

優點:Sentry 自帶豐富的 UI 與聚合功能,開發者只需要在 errorHandler 中呼叫 Sentry.captureException 即可。


常見陷阱與最佳實踐

陷阱 為什麼會發生 解法
錯誤處理本身拋出例外 errorHandler 內部再次呼叫會產生遞迴,最終導致瀏覽器崩潰。 永遠在 errorHandler 中使用 try/catch,或把上報邏輯抽成不會拋錯的 Promise。
Promise 錯誤被 swallow async 函式內未使用 .catch(),錯誤會直接走 unhandledrejection,但若在 errorHandler 中已處理,仍可能遺漏。 同時設置 window.onunhandledrejection,確保任何未捕捉的 rejected 都會被捕獲。
上報延遲造成使用者體驗卡頓 若直接在 UI 執行緒等待 fetch 完成,會阻塞畫面渲染。 使用非同步 fire‑and‑forget(不 await)或在 requestIdleCallback 中上報。
開發環境過度噪音 全域警告與錯誤會在 console 中大量出現,妨礙除錯。 process.env.NODE_ENV === 'production' 時才啟用遠端上報,開發時僅 console.warn
缺乏錯誤上下文 只回報訊息與堆疊,無法定位是哪個 component、哪筆資料觸發。 在 payload 中加入 component 名稱、props、路由資訊,如前面的 GlobalErrorPayload

最佳實踐總結

  1. 全域註冊一次:在 main.js 立即設定 app.config.errorHandler,避免在子組件重複註冊。
  2. 分層上報:先在本地顯示友善訊息,然後非同步上報;若上報失敗,寫入離線緩衝。
  3. 結合第三方服務:如 Sentry、BugSnag,可省去自行建構 UI 與聚合分析的成本。
  4. 保留開發階段的完整堆疊:在開發環境不要過度壓縮錯誤資訊,方便快速定位問題。
  5. 測試錯誤流程:使用 Jest、Cypress 撰寫「故意拋錯」的測試案例,驗證全域 handler 是否正確觸發。

實際應用場景

場景 為什麼需要全域錯誤處理 實作要點
使用第三方 UI 套件(如 Element‑Plus) 套件內部可能因不相容的瀏覽器或資料格式拋出例外,若未捕捉會導致整個頁面卡死。 errorHandler 中記錄 component 為套件名稱,並在 UI 中顯示「系統暫時無法顯示此區塊」的 fallback。
大量異步 API 呼叫 任何一次失敗都可能導致資料不完整,使用者可能看到空白表格。 在每個 API 呼叫的 .catch() 中手動上報,同時在 unhandledrejection 捕捉漏網之魚。
離線可用的 PWA 使用者在離線狀態下仍可能觸發錯誤(如 IndexedDB 讀寫失敗)。 errorHandler 判斷 navigator.onLine,若離線則寫入本地緩衝,待 online 事件觸發時統一上報。
多語系或動態路由 錯誤訊息若未翻譯會影響使用者體驗。 errorHandler 內根據 router.currentRoute.value.meta.locale 取得當前語系,並使用 i18n 產生本地化的 toast。
管理後台儀表板 後台操作常涉及大量表單驗證與即時資料更新,錯誤必須即時告知管理者。 errorHandler 中加入 userIdrole 等欄位,讓後端可以針對特定帳號做錯誤分析。

總結

全域錯誤邏輯是 Vue3 應用穩定性的基石。透過 app.config.errorHandlerwindow.onunhandledrejection 以及結構化的錯誤 payload,我們可以:

  • 即時捕捉 所有未處理的例外與 Promise 錯誤
  • 提供使用者友善的回饋,避免畫面卡死或無回應
  • 將錯誤資訊上報 給遠端監控平台,縮短排除時間
  • 支援離線緩衝,確保在網路不穩時仍不遺失關鍵錯誤資料

在實作時,務必注意不要在錯誤處理本身產生新錯誤、保持上報的非同步性、以及加入足夠的上下文資訊。結合第三方服務(如 Sentry)或自行打造的 API,都能讓錯誤管理更完整、除錯更高效。

掌握了這套全域錯誤處理機制,你的 Vue3 專案將具備 更好的使用者體驗更快的問題定位,也能在日益複雜的前端生態系中,保持彈性與可靠性。祝開發順利,錯誤遠離!