どこで発生したエラーがVue.config.errorHandler
でキャプチャできて、どれができないのか、できないやつはどうすればいいのかについて。
🎉 Vue.js v2.6 Macross Released!
2019/03/03追記: Vue.jsの2.6.0で、キャプチャ可能なエラーの発生シチュエーションが増えたためアップデートしました。
忙しい人向け
これで一通りキャプチャできるはず。
Vue.config.errorHandler = (err, vm, info) => {
console.log(`Captured in Vue.config.errorHandler: ${info}`, err);
};
window.addEventListener("error", event => {
console.log("Captured in error EventListener", event.error);
});
window.addEventListener("unhandledrejection", event => {
console.log("Captured in unhandledrejection EventListener", event.reason);
});
検証用URL
https://clomie.github.io/vue-error-capturing/
console表示させた状態でボタンぽちぽちしてください。
ソースコード: https://github.com/clomie/vue-error-capturing
対象バージョン
現時点での最新版。
Vue.js: v2.6.8
Nuxt.js: v2.0.0
まとめ
Vue.config.errorHandler
でキャプチャできるエラーは下記で発生したもののみ。
- 描画関数、またはテンプレート
- フィルタ関数
- 算出プロパティ関数
- ウォッチャ
- 各種インスタンスライフサイクルフック (v2.2.0以降)
-
data
関数 (v2.2.5以降) - 各種ディレクティブフック (v2.3.0以降)
-
nextTick
関数のコールバック (v2.3.0以降) - カスタムイベントにバインドしたハンドラ関数 (v2.4.0以降)
- DOMネイティブイベントにバインドしたハンドラ関数(v2.6.0以降)
- async修飾子付きの各種インスタンスライフサイクルフック(v2.6.0以降)
- async修飾子付きのイベントハンドラ関数(v2.6.0以降)
- 上記の関数から直接呼び出された関数
Nuxt.jsの場合、上記に加えて
-
fetch
、asyncData
関数(Nuxt.js v2.0.0以降)
下記で発生したエラーはVue.config.errorHandler
ではキャプチャできない。それぞれwindowオブジェクトにerror
, unhandledrejection
のイベントハンドラを登録することでグローバルにキャプチャできる。
- setTimeoutやsetIntervalなどの非同期コールバック関数 (
error
) - Promise経由で呼び出される処理 (
unhandledrejection
) - イベントハンドラ、ライフサイクルフック以外のasync修飾子付きの関数 (
unhandledrejection
)
Vue.config.errorHandler
Vue.js > API > グローバル設定 > errorHandler
実際にはsrc/core/util/error.jsで処理されています。
handleError
、およびinvokeWithErrorHandling
関数がexportされていて、使用箇所を追ってみると、それぞれどこで発生したエラーをキャプチャしているのかがわかります。
Search · handleError repo:vuejs/vue path:/src/core
https://github.com/search?q=handleError+repo%3Avuejs%2Fvue+path%3A%2Fsrc%2Fcore&type=Code
Search · invokeWithErrorHandling repo:vuejs/vue path:/src/core
https://github.com/search?q=invokeWithErrorHandling+repo%3Avuejs%2Fvue+path%3A%2Fsrc%2Fcore&type=Code
invokeWithErrorHandling
が使われている箇所は、async関数などのPromiseを返す関数のエラーに対応しています。
注意点として、Vue.config.errorHandler
でキャプチャ可能なエラーは、たとえ設定していなかったとしても、後述のerror
、またはunhandledrejection
イベントには伝播しません。
これは、Vue.js内のエラー処理で、ブラウザ上で動作している場合、console.errorに直接エラーを出力しているためです。
https://github.com/vuejs/vue/blob/v2.6.8/src/core/util/error.js#L78
Vue.config.warnHandler
Vue.js > API > グローバル設定 > warnHandler
developmentモードでビルドしている際に、コンソールに出力される[Vue warn]:
で始まるメッセージについては、このハンドラで受け取れます。ただし、productionモードでビルドするとwarnメッセージは出力されないため、このハンドラも無視されます。
実際はsrc/core/util/debug.jsで処理されており、warn
関数としてexportされています。使用箇所は下記のリンクから探せますが、こっちはerrorHandler
よりは多いです。
Search · warn repo:vuejs/vue path:/src/core
https://github.com/search?q=warn+repo%3Avuejs%2Fvue+path%3A%2Fsrc%2Fcore&type=Code
よく遭遇するwarnメッセージとしては下記のようなものが挙げられるかと思います。これらはerrorHandler
ではキャプチャできません。
- template内で使っているコンポーネントのcomponents登録忘れ、コンポーネント名ミス
- template内のプロパティ名ミス
- propsのバリデーションエラー
error イベント
GlobalEventHandlers.onerror - Web APIs | MDN
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror
JavaScriptコード内で発生したキャッチされていないエラーをキャプチャできます。
前述の通り、Vue.config.errorHandler
でキャプチャ可能なエラーはerror
イベントには伝播しません。
コンポーネントに定義したメソッドであっても、setTimeoutやsetIntervalといったネイティブな非同期コールバックからの呼び出し等の場合、Vue.jsのコアライブラリが間に挟まる余地が無いため、errorHandler
ではキャプチャできません。
unhandledrejection イベント
unhandledrejection - Event reference | MDN
https://developer.mozilla.org/en-US/docs/Web/Events/unhandledrejection
Promiseの処理でcatchされていないRejectをキャプチャできます。ただし、Vue.config.errorHandler
でキャプチャ可能なエラーはキャプチャできません。また、対応しているブラウザが限られている点に注意が必要です。
Promise内で発生したエラーについても同様にVue.jsのコアライブラリが間に挟まる余地がないため、errorHandler
ではキャプチャできません。
vm.errorCaptured フック
Vue.js > API > オプション / ライフサイクルフック > errorCaptured
errorHandler
と同じ用途で、コンポーネントに定義するライフサイクルフックとしてerrorCaptured
があります。
子孫コンポーネントで発生したエラーに対して反応する点に注意が必要です。設定したコンポーネント自体で発生したエラーには反応しません。
その他の参考リンク
かずぽんブログ • Vue.js 2017年まとめ & 今後
Vue.jsでフロント側のエラー検知を共通化する - エス・エム・エスキャリア開発者ブログ
Vue.jsのerrorHandlerがいまいちつかめない - Qiita
ユーザのブラウザで起きた JavaScript のエラーを収集する - Qiita