Vue 全局异常处理插件

这周查阅了一些关于vue全局异常处理的文章,自己几种方案并根据自己的需求进行了一定的改造,这里记录一下。

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class ServiceError extends Error {
};

function isPromise(ret) {
return (ret && typeof ret.then === 'function' && typeof ret.catch === 'function');
}

function resetPageStatus(vm) {
if (vm.loading) {
vm.loading = false;
}
}

const errorHandler = (error, vm, info) => {
if (error instanceof ServiceError) {
vm.$_warning(error.message);
} else {
console.error(error);
}
const reset = vm.resetPageStatus || resetPageStatus;
reset(vm);
};

function registerActionHandle(actions) {
Object.keys(actions).forEach((key) => {
let fn = actions[key];
actions[key] = function (...args) {
let vm = this;
let ret = fn.apply(vm, args);
if (isPromise(ret)) {
return ret.catch(function(error) {
errorHandler(error, vm);
});
} else { // 默认错误处理
return ret;
}
};
});
}
const registerVuex = (instance) => {
if (instance.$options['store']) {
let actions = instance.$options['store']['_actions'] || {};
if (actions) {
let tempActions = {};
Object.keys(actions).forEach((key) => {
tempActions[key] = actions[key][0];
});
registerActionHandle(tempActions);
}
}
};
const registerVue = (instance) => {
if (instance.$options.methods) {
let actions = instance.$options.methods || {};
if (actions) {
registerActionHandle(actions);
}
}
};

let GlobalError = {
install: (Vue, options) => {
Vue.prototype.ServiceError = ServiceError;
Vue.config.errorHandler = errorHandler;
Vue.mixin({
beforeCreate() {
registerVue(this);
registerVuex(this);
}
});
Vue.prototype.$throw = errorHandler;
}
};

export default GlobalError;

解析

这个全局的异常处理只是一个初步的模板,主要做了几件事情:

  • 自定义了新的错误类型 ServiceError。
  • 覆盖了Vue的全局异常处理接口(Vue.config.errorHandler),使用了自定义给的errorHandler。
  • 组件加载之前利用beforeCreate hook,去遍历所有methods,如果是普通函数,则直接返回,如果是Promise函数,则在catch函数中调用自定义的errorHandler函数。

通过以上几点,所有未被我们捕获并处理的异常都会走到errorHandler函数中。

那么这个errorHandler函数做了什么呢?

  • 判断是否是我们自定义的错误类型 ServiceError,如果是,则弹出ElementUI的message组件,内容就是ServiceError的errorMessage。
  • 如果是普通的错误,简单在控制台打印。
  • 最终查看页面是否有重置状态的函数,如果有则执行,否则执行默认的状态重置函数,目前该函数只是把loading状态取消。

使用方式

1
2
3
4
//main.js
import Vue from 'vue';
import GlobalErrorCatchPlugin from './plugins/globalErrorCatchPlugin';
Vue.use(GlobalErrorCatchPlugin);

总结

目前只是一个比较简单的版本,目的只是为了如果发生了未被捕获的异常,不会让页面卡在loading状态,并且如果是自定义的错误,我们可以自定义去如何处理。

如果通过Axios去和后台通信,也可以把api单独声明为一个方法,这样整个方法一旦500,也不会影响接下来函数的执行,如:

原本需要这样写的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
async searchTableDataWithDateAndStatusByPage () {
this.loading = true;
this.gridData = [];
try {
const response = await EvtHubInboundMessageGW.qureyEvents(this.createSearchCondition(false));
if (response && response.status === 200) {
this.gridData = response.data;
} else {
throw new Error('refresh failed');
}
} catch (e) {
this.$_warning(this.$t('tips.refreshFailed'));
} finally {
this.loading = false;
}
},

可以改成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async qureyEvents() {
return await EvtHubInboundMessageGW.qureyEvents(this.createSearchCondition(false));
}
async searchTableDataWithDateAndStatusByPage () {
this.loading = true;
this.gridData = [];
this.loading = true;
const response = await this.queryEvents();
if (response && response.status === 200) {
this.gridData = response.data;
} else {
this.$_warning(this.$t('tips.refreshFailed'));
}
this.loading = false;
},

这样不仅看起来舒服一点,没有那么多代码块的嵌套,而且就算this.warning(this._warning(this.t(‘tips.refreshFailed’)); 发生了error,errorHandler中也会帮我们把loading状态取消。

总之,是一次vue全局异常捕获的尝试,还在改进当中。