Vue.js (Vue3)では エラーハンドリングの仕組みとして
が提供されています。
これらを使うことでコンポーネント内で発生したエラーのハンドリングを行うことができます。
onErrorCaptured
各コンポーネントで定義をするComposition API用のエラーハンドラーです。
Options APIの場合はerrorcapturedを参照してください。
以下のように使います。
<script setup lang="ts">
import { onErrorCaptured } from "vue";
onErrorCaptured((error, insttance, info) => {
// error: エラーのインスタンス
// instance: ソースコンポーネント
// info: なんかの情報
// errorの情報をもとにハンドリング
console.log("エラーが発生しました。")
});
</script>
【注意】ハンドリングできるのは子孫コンポーネントのエラー
ドキュメントにも
子孫コンポーネントから伝搬するエラーをキャプチャーしたときに呼び出されるフックを登録します。
とあるように ハンドリングできるのは子孫コンポーネントから伝播するエラーです。
つまり以下のようなコードの場合、ボタンをクリックしてthrowErrorからスローされるエラーはハンドリングされません。
<script setup lang="ts">
import { onErrorCaptured } from "vue";
onErrorCaptured((error, insttance, info) => {
// error: エラーのインスタンス
// instance: ソースコンポーネント
// info: なんかの情報
// errorの情報をもとにハンドリング
console.log("エラーが発生しました。")
});
function throwError() {
// ここでスローしても上のonErrorCapturedには入りません!
throw new Error("エラーだ!")
}
</script>
<template>
<div>
<button @click="throwError">error</button>
</div>
</template>
ハンドリングが行われるのは以下のような場合です。
子コンポーネント
<script setup lang="ts">
function throwError() {
throw new Error("エラーだ!")
}
</script>
<template>
<div>
<button @click="throwError">error</button>
</div>
</template>
親コンポーネント
<script setup lang="ts">
import { onErrorCaptured } from "vue";
import SubComponent from "./SubComponent.vue";
onErrorCaptured((error, insttance, info) => {
// error: エラーのインスタンス
// instance: ソースコンポーネント
// info: なんかの情報
// errorの情報をもとにハンドリング
console.log("エラーが発生しました。");
});
</script>
<template>
<div>
<SubComponent />
</div>
</template>
上記の場合では SubComponent
でエラーがスローされた場合は ParentComponent
でハンドリングが行われます。
伝播を止めたい場合はfalseを返す
先のコードでは エラーハンドリングは行われるのですが、そのままエラーは親に伝播するため、別のエラーハンドラでも処理が行われます。
この伝播を止めたい場合は falseを返却することで止めることができます。
<script setup lang="ts">
import { onErrorCaptured } from "vue";
import SubComponent from "./SubComponent.vue";
onErrorCaptured((error, insttance, info) => {
// error: エラーのインスタンス
// instance: ソースコンポーネント
// info: なんかの情報
// errorの情報をもとにハンドリング
console.log("エラーが発生しました。");
// falseを返すことで伝播を止める。
return false
});
</script>
【注意】全てのエラーのハンドリングが行われるわけではない
ドキュメントにも書かれていますが、ハンドリングが行われるのはVue関連のイベントでエラーがスローされた場合です。
例えばですが以下のような、windowのイベントのコールバックや、setTimeoutのコールバックからエラーがスローされた場合はハンドリングが行われません。
<script setup lang="ts">
import { onBeforeUnmount, onMounted } from "vue";
function throwError() {
throw new Error("エラーだ!");
}
// ---------------------
// リサイズのハンドラからエラー
onMounted(() => {
window.addEventListener("resize", throwError);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", throwError);
});
// ---------------------
// setTimeoutからエラー
function handleClick1() {
setTimeout(() => {
throwError();
}, 1000);
}
</script>
<template>
<div>
<button @click="handleClick1">error1</button>
</div>
</template>
setTimeoutでも以下のようにawait していた場合はハンドリングが行われます。
<script setup lang="ts">
function throwError() {
throw new Error("エラーだ!");
}
// ---------------------
// setTimeoutだがawait している場合
async function handleClick2() {
await new Promise<void>((resolve) => setTimeout(() => resolve(), 1000));
// このErrorは親でハンドリングされる。
throwError();
}
</script>
<template>
<div>
<button @click="handleClick2">error2</button>
</div>
</template>
なので 非同期処理を実行する場合はこれらの内容を意識しておく必要があります。
app.config.errorHandler
アプリケーション内で発生したエラーをハンドリングします。
以下のようにして使います。
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
const app = createApp(App);
app.use(router);
app.config.errorHandler = (error, instance, info) => {
// error: エラーのインスタンス
// instance: ソースコンポーネント
// info: なんかの情報
// errorの情報をもとにハンドリング
console.log("エラーが発生しました2。");
// (例)エラー画面に遷移
router.push("/error");
};
app.mount("#app");
注意点としては onErrorCaptured
と同様で全てのエラーがハンドリングされるわけではありません。
番外編
Vueのエラーハンドラが反応しないエラーについては windowのerror
, unhandledrejection
をハンドリングすることで可能です。
const app = createApp(App);
// 省略
window.addEventListener("error", (event: ErrorEvent) => {
console.log("エラーハンドラでエラーをキャッチ");
console.log(event.error)
// エラー画面に遷移
router.push("/error");
})
window.addEventListener("unhandledrejection", (event: PromiseRejectionEvent) => {
console.log("未ハンドリングのreject");
console.log(event.reason)
// エラー画面に遷移
router.push("/error");
})
app.mount("#app");
逆にこれだけでもよい気がしますが errorHandler
だとその他の情報もハンドラの引数から得ることができるので、そういった情報を使いたい場合は errorHandler
を使うとよいかもしれないですね。
まとめ
以上Vue.jsで提供されているエラーハンドリングの仕組みについての紹介でした。
番外編の内容と組み合わせることでよりよいエラーハンドリングができるようになると思います。