vue.config.errorHandler 错误处理调研

主题

在 vue2.6.x 及之后版本中(仅限于此API未被毁灭性更新前)使用全局 errorHandler 钩子来进行 vue 组件中所抛错误的捕捉与处理。

现状

vue2.6 之前,errorHandler 只能捕捉同步函数抛出的错误,而在实际开发中我们关心得更多的是调用接口时可能抛出的错误,ES7之后通常使用 async await 的方式进行接口调用,对于async函数中抛出的错误errorHandler并不能捕捉到。因此我们使用了装饰器(Decorator)进行错误捕捉,方法可行,痒点在于需要在很多个组件中引入装饰器,然后放置在每一个需要的位置(还有个近乎绝症的“痛点”是 vetur 插件对“不规范使用decorator”的红色警告)。

当前使用示例(尽可能简化版):

// errorConfigs.js
import { debounce } from 'lodash';
 
 
const DEBOUNCE_TIME = 500;
const errorProcessorConfigs = [{
  assert (error) {
    return ErrorAssert.isUserInfoNotExist(error); // ErrorAssert为内部Error断言库,使用了webpack的ProvidePlugin声明
  },
  processor: debounce(() => alert('用户信息不存在'), DEBOUNCE_TIME)
}, {
  assert (error) {
    return ErrorAssert.isAppInfoNotExist(error);
  },
  processor: debounce(() => alert('应用信息不存在'), DEBOUNCE_TIME)
}]
 
 
// errorProcessor.js
import errorProcessorConfigs from '@src/configs/errorConfigs';
 
function errorProcessor(error) {
  let errorProcessorConfig = errorProcessorConfigs.find(config => config.assert(error));
  if (errorProcessorConfig !== undefined) {
    errorProcessorConfig.processor(error);
  }
  throw error; // 处理完之后继续将错误抛出以中断程序流程
};
 
export default errorProcessor;
 
 
// errorCatcher.js
import errorProcessor from './errorProcessor';
 
function errorCatcher(target, name, descriptor) {
  const originFunc = descriptor.value;
  descriptor.value = async function () {
    try {
      return await originFunc.apply(this, arguments);
    } catch (error) {
      return errorProcessor(error);
    }
  };
  return descriptor;
};
 
export default errorCatcher;
 
 
// xxx.vue
/* template */
<script>
import errorCatcher from '@services/errorCatcher';
import checkLogin from '@services/checkLogin';
import resources from '@services/resources';
 
export default {
  @errorCatcher
  async created () {
    await checkLogin();
    this.fetchUserInfo();
  },
  methods: {
    @errorCatcher
    async fetchUserInfo () {
        const userInfo = await resources.user.fetch();
        // ...
    },
  }
};
</script>
/* style */

可行性

官网说明:
从 2.6.0 起,这个钩子也会捕获 v-on DOM 监听器内部抛出的错误。另外,如果任何被覆盖的钩子或处理函数返回一个 Promise 链 (例如 async 函数),则来自其 Promise 链的错误也会被处理。
测试:

// main.js
/* ... */
Vue.config.errorHandler = function (err, vm, info) {
  console.error('error---', err)
  console.info('vm---', vm)
  console.info('info---', info)
}
/* ... */
 
 
// Home.vue
/* template */
<script>
export default {
  async created () {
    const error = new Error('test error');
    error.code = -1;
    throw error;
  }
}
</script>
/* style */

console:
vue.config.errorHandler 错误处理调研_第1张图片

应用

// main.js
import Vue from 'vue';
import errorProcessor from '@services/errorProcessor';
 
 
Vue.config.errorHandler = errorProcessor;
/* ... */
 
 
// xxx.vue
/* template */
<script>
import checkLogin from '@services/checkLogin';
import resources from '@services/resources';
 
export default {
  async created () {
    await checkLogin();
    this.fetchUserInfo();
  },
  methods: {
    async fetchUserInfo () {
        const userInfo = await resources.user.fetch();
        // ...
    },
  }
};
</script>
/* style */

对比

装饰器 errorHandler
优势 1.使用灵活,可多个装饰器组装使用;2.装饰器可以接收参数,进行更高阶的应用; 1.全局设置,一处设置处处受益,使用较为简洁;2. 可以直接拿到 this,获取更多信息,进行更多操作;3. info 可辅助定位错误源;
劣势 1.在每一个需要的函数处都需要使用装饰器进行包装,组件中也需要引入装饰器模块;2.装饰器被babel编译后细微的增加文件体积;3.编译期生效,因此如果 errorProcessorConfigs想引入 router 就需要进行一些特殊处理(如将routes 配置放在 main.js 中添加,或者通过变量方式传入) 1.无法向错误处理中传入参数;2.全局只能有一个,如果想针对不同场景下抛出的同一类 error 进行不同处理,则需要加入判断逻辑,影响函数纯粹性。3.捕捉错误条件限制于同步函数或函数返回promise链。4.watch中调用的异步函数暂时无法被捕捉

总结

装饰器错误处理方式略显繁重,但并不会被errorHandler完全取缔,如果想让errorHandler尽可能的捕捉到出现的错误,则可能需要对代码进行一些更为严格的调整(构建promise链),可以根据实际的场景,将二者结合使用,以产生更大的受益(举个例子:某些场景下的错误可以使用装饰器处理并吞掉,不走全局处理)。

期待提建议和补充·~·

你可能感兴趣的:(前端开发)