在錯誤監控中,經常使用到的方法有:javascript
Promise
的未處理異常 unhandledrejection
事件對於 Vue.js 的錯誤上報須要用其提供的 Vue.config.errorhandler
方法,可是錯誤一旦被這個方法捕獲,就不會外拋到在控制檯。若是須要本身手寫一個錯誤監控,則理論上咱們僅僅將錯誤捕獲並上報,但最好不要阻止錯誤在控制檯中展現,因此本文來分析下當中的二三事。vue
若有興趣,或者您正在爲本身的項目寫錯誤上報的插件,能夠一塊兒來分析看看。本文咱們將從錯誤監控的視角來分析和使用 errorhandler
。java
示例代碼以下:git
export default { created() { let a = null; if(a.length > 1) { // ... } } };
正常狀況下,上述代碼會報錯:github
繼上面後,咱們嘗試經過 Vue.config.errorHander
捕獲:函數
Vue.config.errorHandler = (err, vm, info) => { console.log('進來啦~'); } export default { created() { let a = null; if(a.length > 1) { // ... } } };
而後控制檯就不會對外拋錯:
源碼分析
那麼在錯誤監控系統中,理論上咱們只去捕獲報錯而不去攔截報錯,那麼要怎麼作才能把錯誤外拋到控制檯呢?ui
簡單點,就加個console.error(err)
:this
Vue.config.errorHandler = (err, vm, info) => { console.log('進來啦~'); console.error(err);~~~~ } export default { created() { let a = null; if(a.length > 1) { // ... } } };
控制檯將會獲得下面截圖:
spa
那麼這樣就能夠了,錯誤的上報咱們能夠再Vue.config.errorHandler
中去作,完了再去把 Vue 中的這個「短板」補上,讓錯誤繼續正常拋出來。那麼此時錯誤上報的函數(好比這函數是是captureError()
)應該這麼去調用:
Vue.config.errorHandler = (err, vm, info) => { console.log('進來啦~'); // 錯誤上報到收集報錯的平臺 captureError(err); } export default { created() { let a = null; if(a.length > 1) { // ... } } };
爲了弄清楚 Vue 中對於報錯是怎麼處理的,咱們來看看源碼(註釋也順便寫了):
function globalHandleError (err, vm, info) { // 獲取全局配置,判斷是否設置處理函數,默認undefined // 已配置 if (config.errorHandler) { try { // 執行 errorHandler return config.errorHandler.call(null, err, vm, info) } catch (e) { // \# 若是開發者在errorHandler函數中手動拋出一樣錯誤信息throw err \# 判斷err信息是否相等,避免log兩次 if (e !== err) { logError(e, null, 'config.errorHandler') } } } // 沒有配置,常規輸出 logError(err, vm, info) } function logError (err, vm, info) { if (process.env.NODE_ENV !== 'production') { warn(`Error in ${info}: "${err.toString()}"`, vm) } /* istanbul ignore else */ if ((inBrowser || inWeex) && typeof console !== 'undefined') { console.error(err) } else { throw err } }
先來看 bugsnag-js 中的作法:
module.exports = { name: 'vue', init: (client, Vue = window.Vue) => { if (!Vue) throw new Error('cannot find Vue') // 思考爲何這樣作 const prev = Vue.config.errorHandler const handler = (err, vm, info) => { const handledState = { severity: 'error', unhandled: true, severityReason: { type: 'unhandledException' } } const report = new client.BugsnagReport(err.name, err.message, client.BugsnagReport.getStacktrace(err), handledState, err) report.updateMetaData('vue', { errorInfo: info, component: vm ? formatComponentName(vm, true) : undefined, props: vm ? vm.$options.propsData : undefined }) client.notify(report) if (typeof console !== 'undefined' && typeof console.error === 'function') console.error(err) if (typeof prev === 'function') prev.call(this, err, vm, info) } // 思考爲何這樣作 Vue.config.errorHandler = handler return null } }
再來看 sentry 中的作法:
function vuePlugin(Raven, Vue) { Vue = Vue || window.Vue; // quit if Vue isn't on the page if (!Vue || !Vue.config) return; // 爲何這麼作? var _oldOnError = Vue.config.errorHandler; Vue.config.errorHandler = function VueErrorHandler(error, vm, info) { // ... // 上報 Raven.captureException(error, { extra: metaData }); if (typeof _oldOnError === 'function') { // 爲何這麼作? _oldOnError.call(this, error, vm, info); } }; } module.exports = vuePlugin;
以上。