シングルページアブリ(SPA)を作るための有名なフレームワークに、Vue.js, React, Angularがあります。 これからプロジェクトに採用したり勉強するなら、どちらが良いのかGoogleト…
Vue.jsでエラーが発生した場合に適切にエラーをハンドリングし、ユーザーへエラーメッセージの表示・実行環境に応じたログの出力を行います。
エラーハンドリング
Vue.jsではエラーハンドリングの手段が色々あるため、混乱しがちです。
出来る限りシンプル・スマートに実装する手順を記載します。
エラー伝播ルール
まず初めに、Vue.jsではコンポーネントでエラーが発生した後、そのコンポーネントでハンドリングされないと親へ親へと伝播していきます。
そして、最後までコンポーネントでエラーがキャプチャされないと、グローバル設定のVue.config.errorHandler
へ投げられていきます。
Vue.config.errorHandlerで処理する例を見ますが、Vue.configはVueインスタンスを生成する前に定義するので、エラー内容をVueコンポーネントを介してユーザーへ通知するには一筋縄にはいきません。
この記事ではVue.config.errorHandlerは使わず、errorCaptured
で解消したいと思います。
コンポーネントとエラーの境界を分ける
エラーは子孫のコンポーネントの末端から伝播されてくるので、すべてのコンポーネントのルートに当たるApp.vue
でハンドリングします。
エラーはErrorBoundary.vueのerrorCaptured
でハンドリングし、そのままエラー内容を表示するコンポーネントとして使います。
App.vue
<template> <v-app> <error-boundary :stopPropagation="true"> <router-view /> </error-boundary> </v-app> </template> <script> import ErrorBoundary from './components/ErrorBoundary.vue'; export default { name: 'App', components: { ErrorBoundary }, data: () => ({ // }), }; </script>
サンプルではエラー発生時にはslotによって単純に画面全体にエラーページを表示するようにしてあります。
要件に応じてダイアログ表示するように変更するなど、拡張して使用します。
ErrorBoundary.vue
<template> <div> <slot v-if="err" name="error" v-bind:err="err" v-bind:vm="vm" v-bind:info="info" ><p>Error!</p> <p>info: {{ info }}</p> <p>err: {{ err }}</p> <p>vm: {{ vm.toString() }}</p> </slot> <slot v-else></slot> </div> </template> <script> import logger from '@/logger.js'; export default { name: 'ErrorBoundary', props: { stopPropagation: Boolean, }, data() { return { err: false, vm: null, info: null, }; }, errorCaptured(err, vm, info) { this.err = err; this.vm = vm; this.info = info; logger.error(err); return !this.stopPropagation; }, }; </script>
JavsScriptエラー
Vueファイル内に書いてあったとしても、setTimeout
やsetInterval
などVueが介入できないため、VueではなくJavaScriptとしてエラーをハンドリングする必要があります。
main.jsでwindow.onerror
にハンドリング用の処理をセットします。
ここでは作成したロガークラスへエラーを受け渡します。
あとはエラーをログサーバーへ記録するなりコンソールへ出力するなり、ロガークラスへ委譲します。
main.js
import logger from './logger'; // JavaScript error handling window.onerror = (message, source, lineno, colno, error) => { logger.error(error); return true; };
ロギング
発生したエラーは一般的に実行環境によって扱いが変わります。
サンプルではproduction環境ではlog, debug, info, warn, errorはすべてコンソールに表示されないようにして、ログサーバーに流すようにしています。
それ以外の環境ではすべてコンソールへ出力されます。
要件に応じて拡張していきます。
logger.js
class Logger { constructor() { this.init(); } init = () => { if (process.env.VUE_APP_ENVIRONMENT !== 'production') { this.log = console.log.bind(console); this.debug = console.debug.bind(console); this.info = console.info.bind(console); this.warn = console.warn.bind(console); this.error = console.error.bind(console); this.logToServer = this.error; } else { this.log = this.debug = this.info = this.warn = this.error = () => {}; this.logToServer = err => { // ログサーバー等へ投げる }; } }; } const logger = new Logger(); export default logger;
コメントを書く