クソ雑魚エンジニアのメモ帳

学んだことを書くところ

Vue.jsのエラーハンドリングについて調べた件(前編)

こんばんは。ニトリのホテルスタイル枕が最高すぎて会社に遅刻しそうになるmorifujiです、

VuejsOsaka#2で発表した内容ですが、一部はしょったり早口で説明した部分が多かったので、ここに細かく書いていこうと思います。

検証に使用したコードはこちら デモはこちら スライドはこちら

背景

弊社の場合、新規PJの開発中などはステージング環境を常にクライアントにOPENにして都度都度フィードバックをもらうことにしています。その中での話。

クライアント「なんかトップページでローディングが止まんないよ」

僕「むむ。コンソールのログ見せてもらえませんか?」

クライアント「。。。(ナニイッテンダコイツ)」(ここでレスが帰ってこなくなる)

こんな感じのことがたまーにあります。このやりとりって結構大変なんですよねクライアントによってITリテラシーがバラバラなので、、、しかもこの後にローカルで環境を再現してエラーの内容を把握してってなると、結構時間が無駄になってる気がします。

また、改めて考えるとサーバーのエラーは当然のようにトラッキングして集約するのに、フロントのエラーがほったらかしなのはどうなのとも思いました。そこで今回はVuejsで構築したSPAの上で発生したエラーをトラッキングするための仕組みを考えてみました。

エラーをトラッキングするとなると、考えることは以下の2点になるかなと思います。

  1. どうやって検知する?
  2. どこにエラーの情報を集約する?

どちらも試してみたので、順を追って書いていきます、

どうやって検知する?

ざっくり3種類方法があるようなのでそれぞれ調べてみました。

  • window.onerror
  • errorCaptured
  • Vue.config.errorHandler

window.onerror

Vue.jsはJavascriptの上で動作します。なので、当然Javascript自体のエラー検知の機構を使うことができます。それが window.onerror です。

developer.mozilla.org

使い方はこのような感じ。

window.onerror = function(message, source, line, column, error) {
  console.log('catched by `window.onerror_`', message)
<button onclick="HOGEHOGEHOGE">PureなJavascriptでエラー</button>

この状態でbuttonを押すとonerrorがキャッチします。onerrorの引数はこんな感じ。

引数 説明 サンプルでの引数
message エラーメッセージ(string) "Uncaught ReferenceError: HOGEHOGEHOGE is not defined"
source エラーが発生したJavascriptのファイル名(string) "http://localhost:8080/index"
line エラーが発生したJavascriptの行番号 28
column エラーが発生したJavascriptの列番号 34
error 発生したエラー エラーオブジェクト(下記の画像参照)

f:id:Kouchannel55:20190808005043p:plain

errorCaptured

こちらはVuejs独自のエラー検知の機構で、Vuejsの各Component独自に定義することができます。

vuejs.org

SFCでの使い方はこのような感じ。

<template>
  <div class="hello">Hello World!</div>
</template>

<script>
export default {
  name: 'HelloWorld',
  errorCaptured(err, vm, info) {
    console.log('catched by `CHILD errorCaptured`', err.toString())
  },
}
</script>

dataとかpropsとかと並列に定義できます。

親子関係の各Componentに定義されていてなおかつ子Component内でエラーが発生した場合は、以下の図のように親のComponentの errorCaptured を順番に発火させてエラーが伝播していきます。エラーが発生した孫Componentの errorCaptured は発火しないのがポイントですね、

f:id:Kouchannel55:20190808003413p:plain

このエラー伝播は errorCaptured の返り値を false にすることで途中で止めることができます。上記の例では子Componentから親Componentへのエラー伝播を止めることができます。

f:id:Kouchannel55:20190808003914p:plain

Vue.config.errorHandler

こちらもVuejs独自のエラー検知の機構で、引数も errorCaptured と同一ですが、グローバルに設定する点が全く異なります

vuejs.org

使い方は以下のような感じ。

Vue.config.errorHandler = function(err, vm, info) {
  console.log('catched by `Vue.config.errorHandler`', err.toString())
}

errorCaptured のエラー伝播がルートComponentまで到達した場合は、 Vue.config.errorHandler が発火します。

f:id:Kouchannel55:20190808005754p:plain

ということは途中でエラーの伝播が止まった場合は Vue.config.errorHandler は発火しません

f:id:Kouchannel55:20190808010005p:plain

まとめ

3種類紹介しましたが、これを踏まえて僕なりのエラー検知機構の使い分け場面を考えてみました。

  • window.onerror
    • 画面内でVuejs以外のPureなJavascriptが動いていて、その部分に関してのエラーを検知したい場合に有効
  • errorCaptured
    • Vuejsの階層構造の中で、Componentによってエラー検知処理を分けたい場合に有効
    • 例えば、画面によってエラーの検知処理を変えたい場合や、特定のComponent内のエラーは握りつぶしたい場合とか?
  • Vue.config.errorHandler
    • Vuejs内のエラーを一元的に処理したい場合に有効
    • 例えば、全ての画面でのどんなエラーでも収集したい場合

他にもこう言う用途があるよ!みたいなのがあれば教えてくださいー

残りの 2. どこにエラーの情報を集約する? と言う話は、別記事にしようと思いますのでもう少しだけお待ちくださいー。

ではでは