一、前言
众所周知,Vue有一个配置Vue.config.errorHandler,用于指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和 Vue 实例。可以借助这个函数,进行应用的错误日志记录并做一些自定义的处理,防止出现一些严重异常导致应用挂掉。
一般情况下错误日志可以在前端页面中展示,并且需要记录到数据库中。在异常发生时可以调用后台接口存储到后台,前端查询的实现可以使用状态管理(如果需要持久化存储就配合本地存储,但一般不太建议,因为异常太多的话会影响性能)
二、代码实现
可以通过插件形式实现全局异常处理函数,并挂载到Vue.config.errorHandler上。
首先,配置一个开关,用于控制在开发环境还是生产环境中启用日志。
settings.js
export default { /** * @type {string | array} 'production' | ['production', 'development'] * @description 在什么环境中记录日志,生产环境还是开发环境 * 默认生产环境写入错误日志,避免在开发环境调试期间错误日志过多且影响性能 * 如果需要在开发环境中记录错误日志,可以修改为 ['production', 'development'] */ errorLog: ['production', 'development']}
复制代码
然后,准备一个状态管理相关文件,用于记录日志。
errorLog.js
const state = { logs: []} const mutations = { ADD_ERROR_LOG: (state, log) => { state.logs.push(log) }, CLEAR_ERROR_LOG: (state) => { state.logs.splice(0) }} const actions = { addErrorLog({ commit }, log) { commit('ADD_ERROR_LOG', log) }, clearErrorLog({ commit }) { commit('CLEAR_ERROR_LOG') }} export default { namespaced: true, state, mutations, actions}
复制代码
在store/index.js中引入errorLog模块
store/index.js
import Vue from 'vue'import Vuex from 'vuex'import errorLog from './modules/errorLog' Vue.use(Vuex)const debug = process.env.NODE_ENV !== 'production' export default new Vuex.Store({ modules: { errorLog }, // 在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。 strict: debug})
复制代码
然后写一个插件,将全局异常处理函数挂载在Vue.config.errorHandler上。
utils/error-log.js
import store from '@/store'import { isString, isArray} from './util' import config from '@/setting' const install = function (Vue) { const { errorLog: needErrorLog } = config; function checkNeed() { const env = process.env.NODE_ENV; if (isString(needErrorLog)) { return env === needErrorLog } if (isArray(needErrorLog)) { return needErrorLog.includes(env) } return false } function writeErrorLog({ err, route }) { // ajax 调用后台接口去记录日志 } function errorHandler(err, vm, info, a) { // route: uni-app路由,这里可以修改成自己的内容或去掉 let pages = getCurrentPages(), route = ''; if (pages.length) { route = pages[pages.length - 1].route; } console.error('globalError', err); vm && console.error('globalError-vm', vm); info && console.error(info); route && console.error('page', route); // route: uni-app路由,这里可以修改成自己的内容或去掉 if (checkNeed()) { // Don't ask me why I use Vue.nextTick, it just a hack. // detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500 Vue.nextTick(() => { store.dispatch('errorLog/addErrorLog', { err, info, route // route:uni-app路由,这里可以修改成自己的内容或去掉 }) writeErrorLog({ err, info, route // route:uni-app路由,这里可以修改成自己的内容或去掉 }) }) } } // 挂载在原型上,即可以在其他页面中使用this.$throw Vue.prototype.$throw = errorHandler Vue.config.errorHandler = errorHandler} export default install;
复制代码
至此,已经实现了全局的错误拦截和状态记录。
三、uni-app 优化
如果是uni-app项目,也可以在App.vue中定义生命周期钩子onError执行$thow,这样也能触发全局的错误处理函数。App.vue
export default { onLaunch() { console.log('App-Launch'); }, onShow() { console.log('App Show'); }, onHide() { console.log('App Hide') }, onError(err) { this.$throw(err); }}
复制代码
也可以在页面中使用errorCaptured捕获页面中发生的异常。
errorCaptured (err: Error, vm: Component, info: string) => ?boolean
复制代码
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
// 当捕获一个来自子孙组件的错误时被调用errorCaptured(err, vm, info) { // 页面级异常处理 return false // 全局的config.errorHandler将无法捕获到这里的异常}
复制代码
四、拓展阅读
评论