这周查阅了一些关于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
| 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.t(‘tips.refreshFailed’)); 发生了error,errorHandler中也会帮我们把loading状态取消。
总之,是一次vue全局异常捕获的尝试,还在改进当中。