Vue3 生命週期 – onErrorCaptured 完全指南
簡介
在大型單頁應用程式中,錯誤不只會讓功能中斷,更可能洩漏敏感資訊、破壞使用者體驗。Vue 3 為了讓開發者能在 組件層級 更精細地控制錯誤處理,提供了 onErrorCaptured 生命週期鉤子。它能夠 捕獲子組件拋出的錯誤,並決定錯誤是否要向上冒泡,從而避免錯誤「傳染」到整個應用。
本篇文章將從概念說明、實作範例、常見陷阱到最佳實踐,完整呈現 onErrorCaptured 的使用方式,讓你在開發 Vue 3 應用時,能以安全、可控的方式處理例外情況。
核心概念
1. 為什麼需要 onErrorCaptured?
- 局部化錯誤處理:在 Options API 中,
errorCaptured只能在父層組件捕獲子組件錯誤;Composition API 的onErrorCaptured同樣提供這個能力,同時支援在setup中直接使用。 - 錯誤冒泡控制:回傳
false可阻止錯誤向更上層傳遞,類似瀏覽器的事件冒泡機制。 - 與全局錯誤處理的補完:
app.config.errorHandler處理全局錯誤;onErrorCaptured則是更細粒度的局部捕獲,兩者可以同時存在,形成多層防護。
2. 基本語法
import { onErrorCaptured } from 'vue'
export default {
setup() {
onErrorCaptured((err, instance, info) => {
// err: 捕獲到的 Error 物件
// instance: 發生錯誤的子組件實例
// info: 錯誤發生的來源字串 (e.g., "render", "watcher", "event handler")
console.error('捕獲錯誤:', err, info)
// 回傳 false 可阻止錯誤冒泡
return false
})
}
}
參數說明
err: 真正的Error例外。instance: 發生錯誤的子組件 Vue 實例(可用來取得組件名稱或狀態)。info: 錯誤發生的上下文,常見值有"render"、"watcher"、"event handler"。
回傳值
true(預設) → 錯誤會繼續向上冒泡至更高層的onErrorCaptured或全局錯誤處理器。false→ 錯誤不再向上冒泡,僅在當前層級處理。
3. 與 Options API 的對照
| API | 使用方式 | 何時呼叫 |
|---|---|---|
errorCaptured (Options) |
errorCaptured(err, vm, info) { … } |
組件建立階段,即可使用 |
onErrorCaptured (Composition) |
onErrorCaptured((err, instance, info) => { … }) |
必須在 setup() 中呼叫 |
Tip:如果你同時使用 Options API 與 Composition API,兩者的錯誤捕獲會依照宣告順序依次執行,最後仍會交給全局錯誤處理器。
4. 錯誤冒泡示意圖
RootComponent
├─ ParentA (onErrorCaptured)
│ └─ ChildA1 (throws)
└─ ParentB (onErrorCaptured)
└─ ChildB1 (throws)
ChildA1拋出錯誤 →ParentA捕獲 → 若ParentA回傳false,錯誤不會傳到RootComponent。ChildB1拋出錯誤 →ParentB捕獲 → 若ParentB回傳true,錯誤會繼續傳到RootComponent,最終交給app.config.errorHandler。
程式碼範例
範例 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, '| 來源:', info)
// 阻止錯誤冒泡,讓全局 errorHandler 不會被觸發
return false
})
</script>
// Child.vue
<template>
<button @click="triggerError">觸發錯誤</button>
</template>
<script setup>
function triggerError() {
// 故意拋出錯誤
throw new Error('Child component error')
}
</script>
說明:點擊按鈕時,錯誤會被
Parent.vue捕獲並在 console 中顯示,且不會向上傳遞到根組件或全局處理器。
範例 2️⃣ 結合全局錯誤處理器
// main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 全局錯誤處理
app.config.errorHandler = (err, vm, info) => {
console.error('全局錯誤處理:', err.message, '| 組件:', vm?.$options?.name, '| 來源:', info)
// 可在此上報至監控平台
}
app.mount('#app')
// GrandParent.vue
<template>
<Parent />
</template>
<script setup>
import { onErrorCaptured } from 'vue'
import Parent from './Parent.vue'
onErrorCaptured((err) => {
console.log('祖父組件收到錯誤,決定傳遞')
// 返回 true,讓錯誤繼續冒泡到全局處理器
return true
})
</script>
說明:若
Parent.vue在範例 1 中改為return true,錯誤會依序傳到GrandParent.vue,最後觸發app.config.errorHandler,在此可將錯誤上報至 Sentry、LogRocket 等服務。
範例 3️⃣ 捕獲異步錯誤(Promise)
// AsyncChild.vue
<template>
<button @click="loadData">載入資料</button>
</template>
<script setup>
import { ref } from 'vue'
const data = ref(null)
async function loadData() {
// 假設此 API 會回傳 500 錯誤,拋出例外
const response = await fetch('https://api.example.com/invalid')
if (!response.ok) {
throw new Error('API 請求失敗')
}
data.value = await response.json()
}
</script>
// AsyncParent.vue
<template>
<AsyncChild />
</template>
<script setup>
import { onErrorCaptured } from 'vue'
import AsyncChild from './AsyncChild.vue'
onErrorCaptured((err, instance, info) => {
console.error('捕獲到非同步錯誤:', err.message, '| 來源:', info)
// 仍回傳 false,避免全局處理器收到相同錯誤
return false
})
</script>
說明:即使錯誤發生在
async/await中,onErrorCaptured仍能捕獲。Vue 會把未被try/catch包住的 Promise 拒絕視為渲染錯誤,傳遞給錯誤鉤子。
範例 4️⃣ 多層嵌套的錯誤過濾
// FilteredParent.vue
<template>
<ChildA />
<ChildB />
</template>
<script setup>
import { onErrorCaptured } from 'vue'
import ChildA from './ChildA.vue'
import ChildB from './ChildB.vue'
onErrorCaptured((err, instance, info) => {
// 只過濾 ChildA 的錯誤
if (instance?.type?.name === 'ChildA') {
console.warn('過濾 ChildA 的錯誤:', err.message)
return false // 阻止冒泡
}
// 其他錯誤交給上層或全局處理
return true
})
</script>
說明:透過
instance.type.name可以辨識是哪個子組件拋出的錯誤,實作錯誤過濾策略,讓不同子組件有不同的錯誤處理方式。
範例 5️⃣ 與 errorCaptured 混用(Options API)
// MixedComponent.vue
<template>
<slot />
</template>
<script>
export default {
// Options API 版的錯誤捕獲
errorCaptured(err, vm, info) {
console.log('[Options] 捕獲錯誤:', err.message)
// 仍允許冒泡
return true
},
setup() {
// Composition API 版的捕獲
const { onErrorCaptured } = require('vue')
onErrorCaptured((err) => {
console.log('[Composition] 捕獲錯誤:', err.message)
// 停止冒泡
return false
})
}
}
</script>
說明:同一個組件同時使用兩種 API 時,Composition API 的
onErrorCaptured會在 Options API 之後執行。若兩者回傳的值不一致,最後的返回值(Composition)會決定錯誤是否冒泡。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
忘記在 setup 中呼叫 |
onErrorCaptured 必須在 setup() 裡執行,否則不會註冊。 |
確保所有 Composition API 鉤子放在 setup 中。 |
回傳 undefined |
若回傳值不是 true 或 false,Vue 視為 true(繼續冒泡)。 |
明確 return false 以阻止冒泡,或 return true 表示允許。 |
| 捕獲同步錯誤但忽略異步 | onErrorCaptured 只能捕獲未被 try/catch 包住的錯誤,async 函式內的 catch 會自行處理。 |
只在需要全局捕獲未處理異步錯誤時使用,仍建議在 async 中自行 try/catch。 |
在子組件內部使用 throw 失去堆疊資訊 |
直接 throw new Error 會失去 Vue 處理過程的額外資訊。 |
使用 errorCaptured/onErrorCaptured 內部的 info 參數來補足上下文。 |
| 過度阻止錯誤冒泡 | 若所有子組件錯誤都被阻止,根本無法在全局層面監控問題。 | 僅在確定可以自行處理的情況下返回 false,其餘情況保持 true。 |
最佳實踐
- 分層處理:在 UI 相關的父組件捕獲子組件錯誤,顯示友善訊息;在根組件或全局處理器上報錯誤至監控平台。
- 錯誤分類:利用
info(render、watcher、event handler)或自訂錯誤類別,決定不同的處理策略。 - 保持可讀性:在
onErrorCaptured中盡量只做 記錄 與 決策,避免執行過於複雜的邏輯,讓錯誤路徑清晰。 - 搭配 TypeScript:若使用 TS,請為
err宣告類型Error,並在回傳值前加上/** @returns {boolean} 是否繼續冒泡 */,提升 IDE 提示。 - 測試錯誤流:使用 Jest 或 Vitest 撰寫單元測試,模擬子組件拋錯,驗證父組件的
onErrorCaptured是否正確阻止或傳遞。
實際應用場景
| 場景 | 為何需要 onErrorCaptured |
處理方式 |
|---|---|---|
| 表單驗證錯誤 | 子表單元件驗證失敗時拋出錯誤,父表單需要聚合錯誤訊息。 | 父表單使用 onErrorCaptured 收集錯誤,顯示彈窗或錯誤清單。 |
| 第三方元件失效 | 第三方 UI 元件(如圖表)在資料異常時會拋出例外,若不捕獲會導致整頁崩潰。 | 在包裹第三方元件的容器組件中使用 onErrorCaptured,顯示備用 UI(如「載入失敗」訊息)。 |
| 非同步資料請求 | 多個子組件同時發送 API 請求,任一失敗都不應影響其他子組件。 | 每個子組件自行拋錯,父層 onErrorCaptured 捕獲後只記錄,並允許其他子組件正常運作。 |
| 權限檢查失敗 | 某些子組件在渲染前需要檢查使用者權限,若無權限直接拋錯。 | 父層捕獲後導向登入頁或顯示「無權限」訊息,避免整個頁面白屏。 |
| 微前端/子應用整合 | 主應用載入多個子應用,子應用內部錯誤不應影響主應用。 | 主應用在每個子應用的容器上設定 onErrorCaptured,將錯誤限制在子應用範圍內。 |
總結
onErrorCaptured是 Vue 3 Composition API 中的錯誤捕獲鉤子,提供 局部、可控 的錯誤處理能力。- 透過回傳
false可以 阻止錯誤冒泡,回傳true(或不回傳)則讓錯誤繼續向上層或全局錯誤處理器傳遞。 - 配合
app.config.errorHandler、監控平台與適當的 UI 回饋,可打造 彈性且安全 的錯誤處理機制。 - 使用時要注意 在
setup中註冊、明確回傳布林值,以及 避免過度阻止冒泡,以免錯誤被「隱藏」而失去可觀測性。
掌握 onErrorCaptured 後,你將能在 Vue 3 應用中更細緻地管理例外情況,提升使用者體驗與系統穩定性。祝你開發順利,錯誤皆能被優雅捕獲! 🚀