Vue.jsでエラーハンドリング・キャプチャとロギング処理を経てエラー画面を表示する

Vue.jsでエラーハンドリング・キャプチャとロギング処理を経てエラー画面を表示する

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ファイル内に書いてあったとしても、setTimeoutsetIntervalなど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;

 

プログラミングカテゴリの最新記事