1. エラータイプ
フレームワークとしての必須機能であるエラーハンドリングは、Vueでも重要な役割を果たします。
Vueは、特定のプロセスに対応したエラーハンドリング規則を提供し、一部の過程では内部的なエラーハンドリングを実施します。
主要なエラー原因
- バックエンドAPIエラー
- コード内部の論理エラー
2. エラーハンドリングの実施方法
バックエンドAPIエラー
ネットワークリクエストのレスポンスをインターセプターで一層のチェックを実施します。
ApiClient.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response.status === 401) {
window.location.href = '/login';
} else {
alert('エラーが発生しました');
return Promise.reject(error);
}
}
);
コードの論理問題
グローバルエラーハンドラーの設定
Vueのグローバルエラーハンドラーを設定します。
Vue.config.errorHandler = function (err, vm, info) {
// エラーハンドリングの実装
console.error(`Global error handler caught error: ${err.toString()}`);
}
このハンドラーは、Vueコンポーネントのレンダリングや監視プロセス中のキャッチされなかったエラーを処理します。
バージョンごとにハンドラーのスコープが異なります:
- Vue 2.2.0以降:コンポーネントのライフサイクルフ勾子でのエラーをキャッチ
- Vue 2.4.0以降:Vueカスタムイベントハンドラー内部でのエラーをキャッチ
- Vue 2.6.0以降:v-on DOMリスナー内部でのエラーをキャッチ
ライフサイクルフ勾子
errorCapturedはVue 2.5.0から追加されたフ勾子です。
孫のコンポーネントから発生したエラーを親コンポーネントが捕捉します。
Vue.component('cat', {
template: `
<div>
<h1>猫:</h1>
<slot></slot>
</div>`,
props: {
name: {
required: true,
type: String
}
},
errorCaptured(err, vm, info) {
console.log(`ECキャット: ${err.toString()}\n情報: ${info}`);
return false;
}
});
Vue.component('kitten', {
template: '<div><h1>子猫: {{ invalidFunction() }}</h1></div>',
props: {
name: {
required: true,
type: String
}
}
});
<div id="app" v-cloak>
<cat name="マイキャット">
<kitten></kitten>
</cat>
</div>
親コンポーネントのerrorCapturedフ勾子がエラーを捕捉します。
ECキャット: TypeError: invalidFunction は関数ではありません
情報: render
3. ソースコード分析
ソースコード位置: src/core/util/error.js
export function handleError (err: Error, vm: any, info: string) {
pushTarget();
try {
if (vm) {
let cur = vm;
while ((cur = cur.$parent)) {
const hooks = cur.$options.errorCaptured;
if (hooks) {
for (let i = 0; i < hooks.length; i++) {
try {
const capture = hooks[i].call(cur, err, vm, info) === false;
if (capture) return;
} catch (e) {
globalHandleError(e, cur, 'errorCapturedフ勾子');
}
}
}
}
}
globalHandleError(err, vm, info);
} finally {
popTarget();
}
}
export function invokeWithErrorHandling (
handler: Function,
context: any,
args: null | any[],
vm: any,
info: string
) {
let res;
try {
res = args ? handler.apply(context, args) : handler.call(context);
if (res && !res._isVue && isPromise(res) && !res._handled) {
res.catch(e => handleError(e, vm, info + ` (Promise/async)`));
res._handled = true;
}
} catch (e) {
handleError(e, vm, info);
}
return res;
}
function globalHandleError (err, vm, info) {
if (config.errorHandler) {
try {
return config.errorHandler.call(null, err, vm, info);
} catch (e) {
if (e !== err) {
logError(e, null, 'config.errorHandler');
}
}
}
logError(err, vm, info);
}
function logError (err, vm, info) {
if (process.env.NODE_ENV !== 'production') {
warn(`エラー発生: ${info}\n${err.toString()}`, vm);
}
if ((inBrowser || inWeex) && typeof console !== 'undefined') {
console.error(err);
} else {
throw err;
}
}
4. 結論
- handleErrorは、エラー発生時に関連コンポーネントを検索し、親コンポーネントのerrorCapturedフ勾子を呼び出します。
- グローバルエラーハンドラーは、config.errorHandlerを呼び出し、logErrorを経由してエラー情報を出力します。
- invokeWithErrorHandlingは、非同期エラーをより効率的に処理します。
- logErrorは、開発環境では警告を表示し、非開発環境ではコンソールにエラーを出力します。