如何監(jiān)聽Vue項目報錯的4種方式?
背景
在開發(fā)Vue項目時,使用瀏覽器調(diào)試可以比較清晰的看到報的什么錯、在哪報錯,或者使用console.log()打印出報錯信息,以便快速定位到報錯源頭并解決,但是如果項目上線了又怎么查看呢。對于上線項目來說,一般都是會有代碼混淆以及禁用console.log(),這個時候再使用瀏覽器調(diào)試就有點不太方便了。另一種場景,如果要做一個前端報錯監(jiān)控平臺,那這些報錯信息又應該如何收集呢。本文就重點介紹四種方式,即error、unhandledrejection、errorHandler、errorCaptured,用于監(jiān)聽Vue項目可能遇到的報錯。
onerror
當JavaScript運行時錯誤,包括語法錯誤,則window會觸發(fā)ErrorEvent接口的error事件,并執(zhí)行window.onerror()方法,用于處理監(jiān)聽到的錯誤。
如果是資源加載失敗,包括img的src加載失敗或者引入的script加載失敗,則加載資源的元素會觸發(fā)Event接口的error事件,并指定該元素上的onerror()方法處理錯誤。這些error不會冒泡到window,也就是說window.onerror將無法監(jiān)聽到報錯。
特點:
- 可以監(jiān)聽所有的JavaScript錯誤,也能監(jiān)聽Vue組件的報錯,包括一些異步錯誤
- 無法根據(jù)報錯識別Vue組件的詳細信息,也無法監(jiān)聽已經(jīng)被try/catch捕獲的錯誤
- 無法監(jiān)聽資源加載失敗的報錯
window.onerror
window.onerror接收4個參數(shù),分別是:
- message: 錯誤信息
- source:發(fā)生錯誤的資源
- line:發(fā)生錯誤的行號
- column:發(fā)生錯誤的列數(shù)
- error:Error錯誤對象
完整用法:
window.onerror = function(message,source,line,column,error) { // do something };
如果函數(shù)返回true,則會阻止執(zhí)行默認事件處理函數(shù)。
window.addEventListener('error')
使用事件監(jiān)聽,并在全局監(jiān)聽error事件。使用效果同window.onerror類似,語法有所差異:
window.addEventListener('error', event => {});
注意:此event指的是ErrorEvent類型,包含了有關事件以及具體的錯誤信息
舉個栗子:(本文涉及的示例代碼都是基于Vue項目,下同)
// App.vue // window.onerror是全局監(jiān)聽,因此放在入口是比較合理的,盡管也可以放在其他位置 ... <script> export default { mounted() { window.onerror = function(message,source,line,column,error) { console.log('window.onerror----', message,source,line,column,error); } // 效果與上一個類似,都是掛載到全局,兩者使用其一即可。如果兩者都使用,會重復處理error window.addEventListener('error', event => { console.log('window error on listener---', event); }) } } </script> ...
element.onerror
針對一些資源加載失敗的情況,例如img、script,將會觸發(fā)該元素的onerror()處理函數(shù),并且error不會冒泡到window。遇到這種情況,可以手動拋出異常,就可以被全局異常監(jiān)聽到是資源加載失敗了。
舉個栗子
// child.vue // 圖片資源加載失敗 <img src="123" alt="" @error="event => handleError(event)" /> ... handleError(event) { console.log('handleError-----', event); throw new Error('圖片加載失敗了'); // 手動拋出異常,以便全局事件可以監(jiān)聽到 }
errorHandler
Vue全局錯誤監(jiān)聽處理,所有組件的錯誤信息默認都會匯總到此。
由于errorHandler是全局配置的,因此window.onerror將會“失效”,即errorHandler能捕獲的錯誤,onerror將不能捕獲;errorHandler不能捕獲的異常,onerror將捕獲錯誤。如果errorCaptured函數(shù)返回為false,那么此error將不會傳到errorHandler
舉個栗子
// main.js ... const app = createApp(App) app.config.errorHandler = (err, vm info) => { console.log('errorHandle', err, vm, info); // err,錯誤對象 // vm,發(fā)生錯誤的組件實例 // info,Vue特定的錯誤信息,例如錯誤發(fā)生的生命周期、錯誤發(fā)生的事件 }
errorCaptured
errorCaptured是Vue生命周期中的一個,用于捕獲當前組件的所有后代組件產(chǎn)生的錯誤。函數(shù)如果返回為false,則會阻止error繼續(xù)上傳,全局的錯誤監(jiān)聽將不能捕獲該error;否則,全局的錯誤監(jiān)聽也會再處理error。
此鉤子接收三個參數(shù):
- error:Error錯誤對象
- vm:發(fā)生錯誤的組件實例,可訪問組件屬性
- info:包含錯誤來源信息的字符串
在鉤子函數(shù)中,可以修改組件的狀態(tài)
error傳播規(guī)則(劃重點)
- 默認情況下,如果定義了全局的errorHandler,所有的error都將最終匯總到errorHandler中做統(tǒng)一處理
- 如果一個組件的繼承鏈或父鏈存在多個errorCaptured鉤子,則這些鉤子將會被相同的錯誤逐級喚起。
- 如果當前組件的errorCaptured鉤子本身繼續(xù)拋出錯誤,那么這些新的錯誤和原本的錯誤都將上傳到父級組件的errorCaptured鉤子,以及匯總到errorHandler
- 如果一個errorCaptured鉤子返回了false,則會阻止此error的繼續(xù)向上傳播,也就是說這個error到此就已經(jīng)處理完畢了。這個會阻止其他任何被這個錯誤喚起的errorCaptured鉤子以及全局的errorHandler。
tips:如果errorCaptured本身拋出error,return false也就不會執(zhí)行了。
舉個栗子也許更能說明白
代碼寫的比較簡潔,但是“言簡意賅”
// main.js const app = createApp(App) app.config.errorHandler = (err, vm info) => { console.log('errorHandle', err, vm, info); // errorHandler也會執(zhí)行兩次 // err,錯誤對象 // vm,發(fā)生錯誤的組件實例 // info,Vue特定的錯誤信息,例如錯誤發(fā)生的生命周期、錯誤發(fā)生的事件 } // App.vue ... <!-- 引入父組件,并注冊組件 --> <FatherErrorDemo /> ... errorCaptured: (err, vm, info) => { console.log('根組件 捕獲異常 errorCaptured', err,vm,info) // 根組件的errorCaptured會執(zhí)行兩次,先捕獲FatherErrorDemo鉤子自身的錯誤,然后捕獲childErrorDemo的錯誤 // 因此vm也分別指向FatherErrorDemo和childErrorDemo console.log('根組件 vm', vm.$data); // return false; } // FatherErrorDemo.vue ... <div> this is FatherErrorDemo <!-- 引入子組件,并注冊組件 --> <childErrorDemo /> </div> ... errorCaptured: (err, vm, info) => { console.log('父組件 錯誤捕獲', err,vm,info); console.log('父組件 vm', vm.$data); // vm 指向childErrorDemo實例 this.father(); // 未定義,此處會拋出錯誤。errorCaptured鉤子函數(shù)自身產(chǎn)生錯誤 // return false; } // childErrorDemo.vue ... <div> this is childErrorDemo </div> ... mounted() { this.child(); // 未定義,會拋出錯誤 }
執(zhí)行結果:
根據(jù)執(zhí)行過程可知:
- 每一個error產(chǎn)生后,將從產(chǎn)生error的組件,逐級上傳到父級組件的errorCaptured,直到根節(jié)點,最后匯總到errorHandler。表示error完整的生命周期
- errorCaptured返回false,將會阻止當前error向上傳遞,errorHandler也就收不到此error。(本處未執(zhí)行相關代碼)
- errorCaptured自身的error,將會優(yōu)先向上傳遞,直到“被處理”或者到達errorHandler。然后才會傳遞捕獲到的子組件錯誤。
如何監(jiān)聽異步錯誤
異步錯誤無法直接使用Vue的errorCaptured和errorHandler來監(jiān)聽,根據(jù)異步類型的不同,處理方式有所差異。一般來說,監(jiān)聽異步錯誤,多采用window.onerror,如果是監(jiān)聽Promise的錯誤,則使用unhandledrejection事件來監(jiān)聽。
舉個栗子吧
// main.js const app = createApp(App) app.config.errorHandler = (err, vm info) => { console.log('errorHandle', err, vm, info); // errorHandler不會被調(diào)用 // err,錯誤對象 // vm,發(fā)生錯誤的組件實例 // info,Vue特定的錯誤信息,例如錯誤發(fā)生的生命周期、錯誤發(fā)生的事件 } // App.vue ... <!-- 引入父組件,并注冊組件 --> <FatherErrorDemo /> ... mounted() { window.onerror = function(msg,source,line,column,err) { console.log('window.onerror----', msg,source,line,column,err); return true; } window.addEventListener('unhandledrejection', event => { console.log('監(jiān)聽Promise unhandledrejection', event) }) }, errorCaptured: (err, vm, info) => { // errorCaptured不會被調(diào)用 console.log('根組件 捕獲異常 errorCaptured', err,vm,info) console.log('根組件 vm', vm.$data); // return false; } // FatherErrorDemo.vue ... <div> this is FatherErrorDemo </div> ... mounted() { setTimeout(() => { this.father(); }, 1000); // 異步執(zhí)行 new Promise((resolve, reject) => { this.father(); resolve(); }) }
執(zhí)行結果
由此可見,Vue的鉤子和配置函數(shù)并沒有執(zhí)行,但是window.onerror、unhandledrejection卻可以捕獲到錯誤。
為了不寫重復代碼,setTimeout和Promise我放在一起了。實際上,Promise的產(chǎn)生錯誤且沒有被reject處理,那么可以通過監(jiān)聽unhandledrejection事件來捕獲異常。setTimeout產(chǎn)生的錯誤也只能用window.onerror來捕獲(使用window.addEventListener('error')也是一樣)。
總結
- 本文共總結了4種常見的監(jiān)聽錯誤的方法,監(jiān)聽方法沒有對錯,只有使用場景的不同
- 在Vue環(huán)境中,大多數(shù)錯誤可使用errorHandler配置和errorCaptured鉤子解決,但是不能處理異步錯誤
- 異步錯誤根據(jù)類型的不同,可全局監(jiān)聽error和unhandledrejection事件
- 全局捕獲錯誤,可便于快速排查并定位上線項目的問題
- 可基于錯誤捕獲,實現(xiàn)前端錯誤監(jiān)控頁,也可以用于埋點、數(shù)據(jù)分析等場景。
- 這四種方式通常組合起來使用,會實現(xiàn)更好的效果
到此這篇關于如何監(jiān)聽Vue項目報錯的4種方式 的文章就介紹到這了,更多相關監(jiān)聽Vue項目報錯內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue.js中for循環(huán)如何實現(xiàn)異步方法同步執(zhí)行
這篇文章主要介紹了vue.js中for循環(huán)如何實現(xiàn)異步方法同步執(zhí)行問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02基于iview-admin實現(xiàn)動態(tài)路由的示例代碼
這篇文章主要介紹了基于iview-admin實現(xiàn)動態(tài)路由的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10使用Vue.$set()或者Object.assign()修改對象新增響應式屬性的方法
vue代碼中,只要在data對象里定義的對象,賦值后,任意一個屬性值發(fā)生變化,視圖都會實時變化,這篇文章主要介紹了使用Vue.$set()或者Object.assign()修改對象新增響應式屬性,需要的朋友可以參考下2022-12-12