关于vue.js:Vue项目处理错误上报如此简单

47次阅读

共计 5265 个字符,预计需要花费 14 分钟才能阅读完成。

解决异样的意义

随着网页我的项目越来越简单,许多异样报错很难在开发和测试阶段被发现,只管你可能避开了语法等惯例谬误,但不可避免的是代码在运行时的谬误你仍旧无奈精确意料,假如当初有如下一段 Vue 代码,它在生命周期的 created 阶段异步申请并接管了谬误的数据,可能就会导致页面渲染呈现谬误:

<template>
  {{test.obj.xxx}}
</template>
......
created() {this.getSomeData()
},
methods: {getSomeData() {this.fetch().then((res) => {this.test = res // 假如这是申请的谬误数据})
    },
}

而如果测试人员及时发现了这一谬误的话,当他关上控制台时往往就会立刻下结论了:噢,是前端的锅🙂

事实上真正的我的项目中可能会遇到更多 ” 微妙 ” 的问题,而且如果谬误仅产生在某些用户端,那将无从觉察,于是咱们会想到应该在程序中解决捕捉运行时谬误,将谬误上报至服务器,而后剖析和改良代码来修复曾经产生的谬误。

所以该如何应答并解决可能产生的某些谬误,成为了前端开发的一门必修课,你当然能够在每个代码片段中反复编写 try...catch...、为每个 Promise 都解决 catch,但这未免显得有些狼狈,于是我思考能不能用更优雅的形式,对立解决所有异样,将谬误在全局进行捕捉而后上报剖析。其实在 Vue 中实现这样全局的异样解决并不难,上面看看我是如何做的吧。

如何全局捕捉谬误异样

查问 Vue 文档咱们能够发现全局配置中就有这么一个捕捉谬误的解决钩子 errorHandler,用法很简略:

Vue.config.errorHandler = function (err, vm, info) {
    // `info` 是 Vue 特定的错误信息,比方谬误所在的生命周期钩子 
    // 只在 2.2.0+ 可用 
}

只须要用这个钩子就能够解决大部分 Vue 利用中的谬误(如组件生命周期中的谬误、自定义事件处理函数外部谬误、v-on DOM 监听器外部抛出的谬误),并且回调中自带的 info 参数也标记了这个谬误大略是属于哪类,同时它还能解决返回 Promise 链的谬误,能够说是十分弱小了,然而它也并非能解决所有的异样,否则文章写到这就该完结了 ~ 接下来咱们测试一下。

首先在全局谬误捕捉中输入一下 log,先运行一下结尾的申请数据谬误例子:

Vue.config.errorHandler = function (err, vm, info) {console.log('vue 异样谬误捕捉:', '谬误产生在' + info)
}

能够看到异样胜利被捕捉了,因为咱们模仿了一个数据谬误导致渲染出错,所以谬误产生在 render 层,如果是在函数中的 Promise 产生的谬误呢?咱们试一下:

async created() {await this.getSomeData()
},
method: {async getSomeData() {const res = await this.fetch()
      this.test = res
    },
    fetch() {
      asdasd = 1 // 这里给一个未定义的变量赋值,必定会报错
      return new Promise((resolve) => {// ...... 省略})
    }

没有问题,接下来咱们再试试一个按钮用 v-on 绑定 click,然而成心在办法内制作谬误,看看是什么成果:

<button @click="doSomeThing"> Test </button>
..........
doSomeThing() {aaaaaaaa = 111111 // 这里给一个未定义的变量赋值,必定会报错},

看来事件也能失常捕捉,咱们再试试写一个组件,在组件中自定义一个事件,看看后果如何:

<my-custom-comp @node-click="doSomeThing" />

// 在组件中是 $emit 触发:this.$emit('node-click', item)

这个异样仍旧是被胜利捕捉了,当然生命周期钩子中的谬误异样也都能胜利捕捉,就不多做演示了,到目前为止都没有什么问题,然而如果谬误不产生在 Vue 外部呢?

<button onclick="foo()">bad button</button>

能够看到这个异样没有被顺利捕捉,同样的,如果是内部 JS 代码报错,也都是无奈捕捉的,也就是说这个钩子只能捕捉与 Vue 相关联的事件。

宏工作中的谬误也是无奈捕捉的:

.......
fetch() {return new Promise((resolve) => {setTimeout(() => {
      asd = 1 // 在宏工作的异步中呈现的谬误
      resolve({})
    }, 1000)
  })
},

如果 Promise 异样未被失常解决的话,也是捕捉不到的,如下代码,留神这里 create 没有用 await 形式调用异步办法:

created() {this.getSomeData();
},
methods: {async getSomeData() {await (asdasd = 1);
    },
},

上面咱们就逐个解决这两个场景下的谬误捕捉问题。

解决 JS 的额定谬误

咱们能够用 BOM 提供的全局谬误处理函数 window.onerror 来尝试捕捉,它接管多个参数:

window.onerror = function (message, source, line, column, error) {console.log('全局捕捉谬误', message, source, line, column, error)
}

出错代码:

<button onclick="foo()">bad button</button>

当初 JS 异样谬误都能够被捕捉到了,包含 setTimeout 宏工作的异步谬误也能够被捕捉,但咱们留神到未被失常解决的 Promise 谬误仍不能胜利捕捉。

解决 Promise 谬误

参考 Vueerror.js 的代码,同步工作异样捕捉就是套上一层 try...catch...,这也解释了为什么 Vue 捕捉的谬误不会被全局 window.onerror 再次捕捉,因为曾经在这里抛出了。而异步工作异样解决则是判断如果是 Promise 则把 catch 指向错误处理中:

咱们能够模拟写一个插件,来解决 Vue 实例中 methods 的异样。

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

const handleMethods = (instance) => {if (instance.$options.methods) {let actions = instance.$options.methods || {}
    for (const key in actions) {if (Object.hasOwnProperty.call(actions, key)) {let fn = actions[key]
        actions[key] = function (...args) {let ret = args.length > 0 ? fn.apply(this, args) : fn.call(this)
          if (isPromise(ret) && !ret._handled) {
            ret._handled = true
            return ret.catch((e) => errorHandler(e, this, ` 捕捉到了未解决的 Promise 异样:(Promise/async)`))
          }
        }
      }
    }
  }
}

export default {install: (Vue, options) => {
    Vue.mixin({beforeCreate() {this.$route.meta.capture && handleMethods(this)
      },
    })
  },
}

因为遍历所有办法可能会造成页面性能损失,所以这里我加了一个条件,须要在路由设置 meta 能力开启该组件下的 method 额定异样捕捉。

再试试下面的错误代码,看看后果:

created() {this.getSomeData();
},
methods: {async getSomeData() {await (asdasd = 1);
    },
},

能够被失常捕捉,这种形式的益处是咱们能够把产生谬误的实例信息传进去,如果不想应用这种办法,或是在 Vue3 中应用 setup 形式而不是 options 写法,还能够应用全局的事件监听来捕捉:

window.addEventListener('unhandledrejection', (event) => {console.log('全局捕捉未解决的 Promise 异样', event)
})

残缺代码

errorPlugin.js

function errorHandler(err, vm, info) {console.log('vue 异样谬误捕捉:', '错误信息' + info)
  // TODO: 处理错误上报
}

const handleMethods = (instance) => {if (instance.$options.methods) {let actions = instance.$options.methods || {}
    for (const key in actions) {if (Object.hasOwnProperty.call(actions, key)) {let fn = actions[key]
        actions[key] = function (...args) {let ret = args.length > 0 ? fn.apply(this, args) : fn.call(this)
          if (isPromise(ret) && !ret._handled) {
            ret._handled = true
            return ret.catch((e) => errorHandler(e, this, ` 捕捉到了未解决的 Promise 异样:(Promise/async)`))
          }
        }
      }
    }
  }
}
function isPromise(ret) {return ret && typeof ret.then === 'function' && typeof ret.catch === 'function'}

let GlobalError = {install: (Vue, options) => {
    Vue.config.errorHandler = errorHandler
    // eslint-disable-next-line max-params
    window.onerror = function (message, source, line, column, error) {errorHandler(message, null, '全局捕捉谬误')
      // console.log('全局捕捉谬误', message, source, line, column, error)
    }
    window.addEventListener('unhandledrejection', (event) => {errorHandler(event, null, '全局捕捉未解决的 Promise 异样')
    })
    Vue.mixin({beforeCreate() {this.$route.meta.capture && handleMethods(this)
      },
    })
  },
}

export default GlobalError

main.js 中引入

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

// 引入错误处理插件
import ErrorPlugin from './errorPlugin'
Vue.use(ErrorPlugin)

new Vue({
  router,
  render: (h) => h(App),
}).$mount('#app')

在 Vue3 中应用

一样在 main.js 中引入插件即可:

import {createApp} from 'vue'
import App from './App.vue'
import router from './router'
import ErrorPlugin from './errorPlugin'

createApp(App).use(router).use(ErrorPlugin).mount('#app')

完结

如果你须要更加丰盛的谬误收集剖析性能,还是得应用如 Sentry、Bugsnag 这类欠缺的谬误追踪服务,不过绝对来讲这些都须要不少配置部署操作。本文介绍了如何简略地在 Vue 中全局捕捉异样谬误,晋升代码健壮性,且能防止在代码中编写大量异样捕捉块,同时也缩小了出错时控制台的大片飘红报警,收集谬误能够帮忙咱们定位开发与测试阶段不易发现的疑难杂症,这部分能够应用 http 申请将错误信息发送到服务器。

以上就是文章的全部内容,心愿对你有所帮忙!如果感觉文章写的不错,能够点赞珍藏,也欢送关注,我会继续更新更多前端有用的常识与实用技巧,我是茶无味 de 一天,心愿与你独特成长~

正文完
 0