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