前端異常收集和處理

什麼是異常(Exception)?

程序運行過程中,破壞程序正常控制流程的事件。

前端中異常的類型

  • Error:基類型;
  • ReferenceError:找不到對象時拋出;
  • SyntaxError:語法錯誤;
  • TypeError:類型錯誤時拋出;
  • EvalError:eval()函數發生異常時拋出;
  • InternalError :js引擎內部發生的錯誤;
  • RangeError:數值超出範圍時觸發;
  • URIError:URI格式不正確時拋出,常發生在encodeURI或者decodeURI調用時;
  • DOMException:調用web api屬性時發生的異常事件 

還有就是css異常,不會拋錯阻塞ui線程,但是樣式不會生效,所以無傷大雅。在.html文件裏,比如標籤寫錯了,沒收尾,這些錯誤也不會阻塞ui線程的渲染。但是在.vue文件裏面,因爲要過vue-loader,vue-loader編譯模板的時候,會強校驗html的標籤是否閉合。這就跟在.ts文件裏,要過ts-loader是一個道理,不過這時候爆出的大都是語法錯誤,不修復就沒法投入生產,只要錯誤不上線,都好說。

那麼,請問以下腳本執行會出錯麼?拋什麼錯誤呢?

// 片段1:
Let b;
b.a;
// 片段2:
const a = 1;
a =2;

無論是發生在編譯過程中、運行時、還是邏輯錯誤,最後都會在命令行或者瀏覽器控制檯拋出錯誤詳情。
那ECMAScript內建的錯誤機制是什麼?錯誤拋出時候會怎麼處理錯誤信息呢?我們都知道js引擎是通過上下文棧來存儲函數的,先進後出,當錯誤發生時,就會沿着函數上下文站向上冒泡,直到找到指定的錯誤代碼處理爲止,也就是在這時候拋出錯誤。

而且 js是單線程的,任何異常都會導致進程阻塞,那怎麼處理異常,才能讓異常不阻塞進程,還能方便修復異常呢?

常見的錯誤處理方式try-catch

try中的詞法作用於:https://www.ecma-international.org/ecma-262/10.0/index.html#sec-try-statement
try-catch適合處理一些無法控制的錯誤,比如網絡請求、各種I/O操作等。將有可能發生錯誤的代碼放在try語句塊中,一旦發生異常,就將控制權轉交給catch塊執行,然後繼續執行後續任務,不會阻塞進程。但是try塊中的異步、語法錯誤,catch是捕獲不到的,所以需要單獨處理一次。Vue中也對異步的錯誤做了單獨處理,這個我們後面再看。
比如下面這段代碼中的異步error:

 

try語句塊中setTimeout任務中會拋出錯誤,但是因爲js是基於事件循環機制的,當try-catch運行完之後,纔會在循環隊列裏取出setTimeout任務執行,因此catch是捕獲不到異步錯誤的。
再看這段中的語法error: 

 

 不過在開發環境下,語法錯誤,一般都會被編輯器拋出,並且會導致程序的崩潰,能得到及時的修復。

常見的錯誤捕獲方式window.onerror:

常見的運行時錯誤捕獲方式就是window.onerror了,js運行時,沒有被try-catch住的錯誤,一般會觸發window.onerror事件,接受的參數包括錯誤信息msg、發生錯誤的腳本url、發生錯誤的行號row列號col、Error對象,Error裏面包含錯誤堆棧等詳細信息。當然,window.onerror事件也是捕獲不到語法錯誤的。
其實window.addEventListener("error", e => {});也可以捕獲錯誤,但是返回的是個ErrorEvent事件對象,錯誤信息不如onerror事件完整。

常見錯誤捕獲方式window.unhandledrejection

從es6開始,js支持promise對象,允許控制延遲和異步的操作流。Promise有pending、fulfilled、rejected、settled四種狀態,前三種大家經常見,第四種是指promise處於fulfilled或者rejected二者的任意一個狀態的時候。發生在promise塊中的錯誤,無論是否catch,都會觸發unhandledrejection事件。

 

 如上圖代碼片段所示,window.onerror它其實是捕獲不到promise reject拋出的異常的。如果在promise裏沒有手動catch異常,則該異常就會被漏報。

看完了同步的和異步的,那如果跨域的資源出錯,怎麼辦?

跨域資源出錯怎麼辦?

script Error?當引用第三方script的時候,腳本出現錯誤,直接提示“script error”。

解決辦法1:設置crossorigin=”anonymous”,在非同源情況下,設置 "anonymous" 關鍵字將不會通過 cookies,客戶端 SSL 證書或 HTTP 認證交換用戶憑據。但是這個屬性並不是所有瀏覽器都支持(CORS settings attributes - HTML(超文本標記語言))。
解決辦法2:將第三方腳本方法放在try-catch裏。

 

vue的errorHandler和errorCaptured

vue主要是通過errorCaptured鉤子來捕獲錯誤,通過errorHandler處理錯誤。
errorCaptured是捕獲一個來自子孫組件的錯誤時被調用。此鉤子會收到三個參數:err錯誤對象、vm發生錯誤的組件實例,info一個包含錯誤來源信息的字符串。此鉤子如果返回 false ,該錯誤句不用繼續向上傳播。


性能:

在日常開發中,window.onerror主要是捕獲意外的錯誤,而try-catch是監控可能發生的特定錯誤。其實現有的錯誤處理工具,比如sentry,都是封裝了一層try-catch,那try-catch一定會延長執行棧,但是會不會影響代碼的性能呢?

 

看上面倆例子,都是一千萬次求和。不加try-catch的性能和加了try-catch的差距其實並不大。因爲chrome v8引進了新的優化引擎TurboFan,優化了try/catch/finally、for...of 等語法,還支持了es6+的一些新語法,所以不用擔心try-catch的性能問題。但是也要合理使用,比如,我們看下圖第三個示例,將for循環放在catch塊之後,性能大幅度下降。

 

 因爲V8(https://v8docs.nodesource.com/node-0.8/d4/da0/v8_8h_source.html)引擎更換了新的編譯器TurboFan。在 v8 優化之前,前端 try catch 存在挺大的性能問題。

js引擎會拷貝當前的詞法環境,拷貝的其實就是當前 scope 下的所有的變量。
引擎V8以及技術優化:https://blog.csdn.net/hgl868/article/details/45095153

提前規避邏輯錯誤的一個辦法--自動化測試:

vue中的應用,詳情見:https://vue-test-utils.vuejs.org/

 

 參考:
常見錯誤類型: https://www.ecma-international.org/ecma-262/10.0/index.html#sec-native-error-types-used-in-this-standard-referenceerror
駁《慎用 try catch》:https://juejin.im/post/5c199882f265da617464c745 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章