Vue 3 のエラー処理とデバッグ:errorHandler、Vue DevTools、Sentry、およびパフォーマンスの最適化
エラー処理は、あらゆる本番環境向けのアプリケーションにとって不可欠な機能です。これにより、クラッシュの原因を迅速に特定し、機能を適切に制限し、ユーザーに通知することが可能になります。Vue 3 では、errorHandler(グローバル)、onErrorCaptured(ローカル)、および errorCaptured フック(コンポーネントレベル)という、多段階のエラー処理が提供されています。
デバッグとパフォーマンスの最適化はどちらも同様に重要です。Vue DevToolsは、コンポーネントツリー、状態、イベント、パフォーマンスのボトルネックを確認できる、欠かせないツールです。
1. 学習内容
- 3層のエラー処理:errorHandler / onErrorCaptured / errorBoundary
- Vue DevToolsの4つの主要機能(コンポーネントツリー/ステート/イベント/パフォーマンス)
- パフォーマンス分析(パフォーマンス API + DevTools)
- Sentryとの連携(本番環境のエラー監視)
- ソースマップの場所が間違っている
- よくあるデバッグのコツ5選
2. 「特定できる原因がない」とされる「ホワイトスクリーンクラッシュ」の事象
(1) 課題:1つのAPIでエラーが発生すると、ページ全体が真っ白になってしまう
Aliceの管理画面には、1つの重大なバグがありました:
JS
// ProductList.vue
const products = ref([])
onMounted(async () => {
const res = await fetch('/api/products')
products.value = await res.json() // ❌ The backend returns non-JSON → throws error
})
ユーザー体験:
- 「ProductList」ページ全体が真っ白な画面で表示される
- エラーメッセージはコンソールにのみ表示される
- ユーザーには空白のページが表示され、ページを更新しても解決しない
プロダクトマネージャーのチャーリー:
「アリス、顧客からページが真っ白になるという苦情が寄せられています。1)ページ全体がクラッシュしないようにし、2)エラーメッセージを表示し、3)監視システムにログを記録する必要があります。」
(2) Vueのエラーバウンダリーに対する解決策
VUE
<!-- Parent Component:Error Boundary -->
<template>
<ErrorBoundary>
<ProductList />
</ErrorBoundary>
</template>
<!-- ErrorBoundary.vue -->
<script setup>
import { onErrorCaptured, ref } from 'vue'
const error = ref(null)
onErrorCaptured((err, instance, info) => {
console.error('Caught:', err)
// 1. Display error message
error.value = err.message
// 2. Report to Sentry
Sentry.captureException(err)
// 3. Prevent upward transmission
return false
})
</script>
<template>
<div v-if="error" class="error">
<h3>⚠️ Something went wrong</h3>
<p>{{ error }}</p>
<button @click="retry">Retry</button>
</div>
<slot v-else />
</template>
ユーザー体験:
- エラーは捕捉されたため、伝播することはありません
- ユーザーにわかりやすいエラーページを表示する
- Sentryへの自動エラー報告
- ユーザーは「再試行」をクリックできます
(3) 収益
エラー処理の後:
- ホワイトスクリーンによるクラッシュ:100% → 0
- エラーの発生場所:コンソール → Sentry ダッシュボード
- ユーザー体験:クラッシュ → 親切なメッセージ
- 可観測性:すべてのエラーが記録される
3. 3段階のエラー処理
(1) グローバルなエラー処理:app.config.errorHandler
JS
// main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// ✅ Global Error Handling
app.config.errorHandler = (err, instance, info) => {
console.error('Global error:', err)
console.log('Component:', instance)
console.log('Info:', info) // 'render' / 'watch' / 'lifecycle hook'
// Report to Sentry
Sentry.captureException(err)
// User Notification
showErrorNotification('Something went wrong')
}
(2) コンポーネントレベルのエラー:onErrorCaptured
VUE
<!-- ErrorBoundary.vue -->
<script setup>
import { onErrorCaptured, ref } from 'vue'
const error = ref(null)
// ✅ Catching Child Component Errors
onErrorCaptured((err, instance, info) => {
console.error('Boundary caught:', err)
error.value = {
message: err.message,
stack: err.stack,
info
}
// Prevent upward transmission
return false
})
</script>
<template>
<div v-if="error" class="error-boundary">
<h3>⚠️ {{ error.message }}</h3>
<details>
<summary>Stack trace</summary>
<pre>{{ error.stack }}</pre>
</details>
<button @click="reset">Retry</button>
</div>
<slot v-else />
</template>
(3) 誤差レベルの比較
| レベル | API | 適用範囲 | 対象 |
|---|---|---|---|
| グローバル | app.config.errorHandler |
すべての未処理のエラー | 本番環境必須 |
| コンポーネント | onErrorCaptured |
子コンポーネントのエラー | ローカルエラー境界 |
| 非同期 | try/catch / window.onunhandledrejection | Promiseエラー | 手動での処理 |
(4) 誤りの5つの主な原因
JS
// 1. Rendering Error(Template syntax error)
app.config.errorHandler = (err, instance, info) => {
if (info === 'render') console.error('Render error:', err)
}
// 2. Lifecycle Hook Error
if (info === 'mounted') console.error('Mounted error:', err)
// 3. watch Callback Error
if (info === 'watcher callback') console.error('Watch error:', err)
// 4. Custom Command Error
if (info === 'directive') console.error('Directive error:', err)
// 5. Error Handling
if (info === 'v-on handler') console.error('Event error:', err)
4. DevTools ビュー
(1) 4つの主要コアパネル
| パネル | 機能 | 目的 |
|---|---|---|
| コンポーネント | コンポーネントツリー、状態、プロパティ、エミット | コンポーネントの構造の理解、データのデバッグ |
| タイムライン | イベント、ライフサイクル、パフォーマンス | イベントの流れとパフォーマンスのボトルネックを追跡 |
| Pinia/Vuex | 状態の保存 | グローバル状態のデバッグ |
| ルート | ルートの履歴 | ルートのリダイレクトをデバッグ |
(2) コンポーネントパネルの使用方法
TEXT
1. Open DevTools(F12 / Cmd+Opt+I)
2. Switch to Vue Tags
3. Click a component in the component tree
4. Display on the right:
- State(Responsive Data)
- Props
- Emits
- Slots
- Lifecycle
(3) DevToolsの役立つヒント5選
JS
// 1. Real-time editing state(Development Mode)
// DevTools Edit directly in the document,Watch the component respond in real time
// 2. Time Travel(Pinia)
// Switch to Pinia Panel → Base → You can replay each step state Changes
// 3. Performance Labels
// import { markRaw } from 'vue'
// Big Data Objects Used For markRaw Mark,DevTools Does not perform a deep traversal
// 4. Components highlight
// DevTools → top right corner"Eyes"Icon → Highlight Components on Mouse Hover
// 5. Route Redirection
// DevTools → Routing Tags → Look at each redirect path + params
(4) Vue DevTools をインストールする
BASH
# Chrome Extensions
# https://chromewebstore.google.com/detail/vuejs-devtools/odjccnclnlddjlajjphfdmhnlhaglgki
# Firefox Extensions
# https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/
# or a standalone app(Recommendations)
# https://devtools.vuejs.org/
5. パフォーマンス分析
(1) View 3 Performance API
JS
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
performance.mark('app-start')
// Your code
loadData()
performance.mark('app-end')
performance.measure('app-load', 'app-start', 'app-end')
const measure = performance.getEntriesByName('app-load')[0]
console.log(`App loaded in ${measure.duration}ms`)
})
(2) パフォーマンス最適化の5つのヒント
JS
// 1. v-once:Render only once
<h1 v-once>{{ title }}</h1>
// 2. v-memo:Cache Subtree
<div v-memo="[item.id, item.updatedAt]">
<!-- Only re-render on id or updatedAt changes -->
</div>
// 3. shallowRef:Big Data Does Not Respond Deeply
const bigList = shallowRef([...10000 items])
// 4. markRaw:Third-party library is not responding
const map = markRaw(new Map())
// 5. Lazy Loading:Routing / Components
const Heavy = defineAsyncComponent(() => import('./Heavy.vue'))
(3) 5つの主要業績評価指標
| 指標 | 目標値 | 測定方法 |
|---|---|---|
| FCP (First Contentful Paint) | 1.8秒未満 | Lighthouse |
| LCP (Largest Contentful Paint) | 2.5秒未満 | Lighthouse |
| TTI (Time to Interact) | < 3.8秒 | Lighthouse |
| TBT (総ブロッキング時間) | < 200 ms | Lighthouse |
| CLS(レイアウトシフト) | < 0.1 | Lighthouse |
6. Sentryとの連携
(1) インストール
BASH
npm install @sentry/vue @sentry/tracing
(2) main.js の設定
JS
import { createApp } from 'vue'
import * as Sentry from '@sentry/vue'
import { Integrations } from '@sentry/tracing'
import App from './App.vue'
// 1. Initialization Sentry
Sentry.init({
app,
dsn: 'https://your-dsn@sentry.io/123',
integrations: [
new Integrations.BrowserTracing()
],
tracesSampleRate: 1.0,
// Reduce Sampling in the Production Environment
// tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0
// Vue Specific Placement
logErrors: true,
release: '1.0.0',
environment: process.env.NODE_ENV
})
const app = createApp(App)
// 2. Global Error Reporting
app.config.errorHandler = (err, instance, info) => {
Sentry.captureException(err, {
extra: {
component: instance?.$options.name,
info
}
})
}
(3) ソースマップの設定
JS
// vite.config.js
export default {
build: {
sourcemap: true // Generated in the production environment as well source map
}
}
// Sentry Upload source map
// @sentry/cli releases -o your-org -p your-project files upload-sourcemaps ./dist
(4) キーセントリーの5つの主な機能
| 機能 | 目的 |
|---|---|
| エラー監視 | すべてのエラーを自動的に記録し、ダッシュボードで確認できます |
| パフォーマンストレース | 処理に時間がかかるリクエストやデータベースクエリの特定 |
| セッション再生 | ユーザーの操作を記録して、エラーが発生した箇所を確認する |
| 健康関連のリリース | リリースごとのエラー率の比較 |
| アラート | Slack / メール通知 |
7. 完全な例:5つの重要なデバッグ手法
(1) ▶ サンプル:. 3段階のエラー処理
JS
// main.js:Global
app.config.errorHandler = (err, instance, info) => {
console.error('Global:', err, info)
Sentry.captureException(err)
}
VUE
<!-- ErrorBoundary.vue:Components -->
<script setup>
import { onErrorCaptured, ref } from 'vue'
const error = ref(null)
onErrorCaptured((err, instance, info) => {
error.value = err.message
return false
})
</script>
JS
// Asynchronous:Manual try/catch
try {
await fetch('/api/data')
} catch (err) {
console.error('Async:', err)
Sentry.captureException(err)
}
(2) ▶ サンプル:. 5つの主な誤りの原因
JS
// 1. Rendering Error
if (info === 'render') console.error('Render')
// 2. Life Cycle Errors
if (info === 'mounted') console.error('Mounted')
// 3. watch Error
if (info === 'watcher callback') console.error('Watch')
// 4. Command error
if (info === 'directive') console.error('Directive')
// 5. Event Error
if (info === 'v-on handler') console.error('Event')
(3) ▶ サンプル:. DevTools ビュー:4つのパネル
| パネル | 目的 |
|---|---|
| コンポーネント | コンポーネントツリー + 状態 + プロパティ |
| タイムライン | イベントストリーム + ライフサイクル |
| ピニア | 店舗の状況 |
| ルート | ルートの履歴 |
(4) ▶ サンプル:. 5つの主要なパフォーマンス最適化
JS
// 1. v-once
<h1 v-once>{{ title }}</h1>
// 2. v-memo
<div v-memo="[item.id]">...</div>
// 3. shallowRef
const list = shallowRef([...])
// 4. markRaw
const map = markRaw(new Map())
// 5. Lazy Loading
const Heavy = defineAsyncComponent(() => import('./Heavy.vue'))
(5) ▶ サンプル:. よくある5つの間違いに関するクイックリファレンス
| エラー | 症状 | 解決策 |
|---|---|---|
| ホワイトスクリーンによるクラッシュ | 1つのコンポーネントのエラーがすべてをクラッシュさせる | ErrorBoundaryでラップする |
| エラーが捕捉されました | 子コンポーネントが静かに失敗します | onErrorCaptured で false を返すのを忘れないでください |
| ソースマップが見つかりません | 本番環境のエラーを特定できません | vite build sourcemap: true |
| パフォーマンスの低下 | 1,000件のアイテムを一覧表示する際に遅延が発生 | v-memo + shallowRef |
| メモリリーク | コンポーネントの破棄後もタイマーが動作し続ける | onUnmounted でのクリーンアップ |
(6) ▶ サンプル:5つの主要なデバッグシナリオ
| シナリオ | ツール |
|---|---|
| コンポーネントの状態エラー | Vue DevTools → 状態 |
| イベントがトリガーされなかった | Vue DevTools → タイムライン |
| ルーティングのリダイレクトエラー | DevTools → ルーティング |
| 本番環境のクラッシュ | Sentry ダッシュボード |
| パフォーマンスの低下 | Lighthouse + DevTools によるパフォーマンス |
❓ よくある質問
Q
errorHandler は非同期エラーを捕捉できますか?A いいえ。
errorHandler は Vue 内の同期エラーのみを捕捉します。非同期エラー(Promise / setTimeout)を捕捉するには、try/catch または window.addEventListener('unhandledrejection') が必要です。Q
onErrorCaptured は false を返さなければならないのでしょうか?A いいえ、その必要はありません。
false を返すことで、エラーが上流へ伝播するのを防ぐことができます。true を返す(あるいは何も返さない)と、エラーは上流のグローバル変数 errorHandler へと伝播し続けます。Q Vue DevToolsは本番環境で使用できますか?
A はい。本番ビルドでは、DevToolsが自動的に有効になります(パフォーマンスに影響はありません)。ただし、本番環境では、内部構造が外部にさらされるのを防ぐため、
Vue.config.devtools = false を使用して無効にすることをお勧めします。Q Sentryの料金体系はどのようになっていますか?
A 無料プランでは、毎月5,000件のイベントと10,000件のパフォーマンストランザクションが利用可能です。これは中規模のプロジェクトには十分な容量です。この上限を超える利用分については、従量課金制で請求されます。
Q v-memo と v-for はどのように連携するのですか?
A
<div v-for="item in items" :key="item.id" v-memo="[item.id, item.updatedAt]">、この div は、id または updatedAt のいずれかが変更された場合にのみ再レンダリングされます。Q SSRアプリケーションのデバッグ方法は?
A VS CodeのデバッガーとNode.jsのインスペクトモードを使用します。あるいは、Sentryを使ってサーバーサイドのエラーを捕捉することもできます。また、Nuxt 3のNitroデバッグツールを使用することも可能です。
Q Vue 3とVue 2における
onErrorCapturedの違いは何ですか?A Vue 2では
errorCapturedフックですが、Vue 3ではonErrorCapturedコンポジションAPIです。また、Vue 3では新しいグローバル変数app.config.errorHandlerも導入されています。📖 まとめ
- 3段階のエラー処理:グローバル(errorHandler)+コンポーネント(onErrorCaptured)+非同期(try/catch)
- エラーの主な原因となる5つの要素:レンダリング/ライフサイクル/ウォッチ/ディレクティブ/イベント
- Vue DevTools 4 のパネル:コンポーネント / タイムライン / Pinia / ルート
- 5つの主要なパフォーマンス最適化:v-once / v-memo / shallowRef / markRaw / 遅延読み込み
- Sentryとの連携:自動キャプチャ + パフォーマンス追跡 + ソースマップ
- 5つの主要なパフォーマンス指標:FCP/LCP/TTI/TBT/CLS
- 本番環境に欠かせないもの:ErrorBoundary + Sentry + ソースマップ
📝 練習問題
(1) 基礎問題(難易度:⭐)
ErrorBoundary コンポーネントを実装します:
- onErrorCaptured: 子コンポーネントからのエラーを捕捉します
- ユーザーにわかりやすいエラーページを表示する
- 「再試行」ボタンを表示する
(2) 上級問題(難易度:⭐⭐)
包括的なエラー処理システムを実装する:
- グローバル変数
errorHandlerがコンソールに出力されます - コンポーネントレベルのエラーバウンダリー
- 非同期エラー:try/catch
- 誤差要因の5つの分類
(3) チャレンジ問題(難易度:⭐⭐⭐)
包括的な「本番環境向け」のデバッグおよびエラー監視を実装する:
- ErrorBoundary コンポーネント + 3つの異なる場所
- Sentryとの連携(本番環境)
- ソースマップの設定
- Vue DevTools におけるデバッグの5つのヒント
- 5つの主要なパフォーマンス最適化(v-memo / shallowRef / markRaw / 遅延読み込み / v-once)
- 5つの主要業績評価指標のモニタリング(Lighthouseとの連携)



