作好錯誤監控,將用戶使用時的錯誤日誌上報,能夠幫助咱們更快的解決一些問題。目前開源的比較好的前端監控有html
那前端監控是怎麼實現的呢?要想了解這個,須要知道前端錯誤大概分爲哪些以及如何捕獲處理。前端
前端錯誤分爲JS運行時錯誤、資源加載錯誤和接口錯誤三種。vue
JS運行時錯誤通常使用window.onerror捕獲,可是有一種特殊狀況就是promise被reject而且錯誤信息沒有被處理的時候拋出的錯誤webpack
使用window.onerror和window.addEventListener('error')捕獲。其中window.onerror含有詳細的error信息(error.stack),並且兼容性更好,因此通常JS運行時錯誤使用window.onerror捕獲處理git
window.onerror = function (msg, url, lineNo, columnNo, error) { // 處理error信息 } window.addEventListener('error', event => { console.log('addEventListener error:' + event.target); }, true); // true表明在捕獲階段調用,false表明在冒泡階段捕獲。使用true或false均可以
例子: https://jsbin.com/lujahin/edit?html,console,output 點擊button拋出錯誤,分別被window.onerror和window.addEventListener('error')捕獲
當promise被reject而且錯誤信息沒有被處理的時候,會拋出一個unhandledrejection,而且這個錯誤不會被window.onerror以及window.addEventListener('error')捕獲,須要用專門的window.addEventListener('unhandledrejection')捕獲處理github
window.addEventListener('unhandledrejection', event => { console.log('unhandledrejection:' + event.reason); // 捕獲後自定義處理 });
https://developer.mozilla.org...
例子: https://jsbin.com/jofomob/edit?html,console,output 點擊button拋出unhandledrejection錯誤,而且該錯誤僅能被window.addEventListener('unhandledrejection')捕獲
一些特殊狀況下,還須要捕獲處理console.error,捕獲方式就是重寫window.console.errorweb
var consoleError = window.console.error; window.console.error = function () { alert(JSON.stringify(arguments)); // 自定義處理 consoleError && consoleError.apply(window, arguments); };
例子: https://jsbin.com/pemigew/edit?html,console,output
什麼是跨域腳本error?api
https://developer.mozilla.org...
當加載自不一樣域的腳本中發生語法錯誤時,爲避免信息泄露(參見bug 363897),語法錯誤的細節將不會報告,而代之簡單的"Script error."。在某些瀏覽器中,經過在<script>使用crossorigin屬性並要求服務器發送適當的 CORS HTTP 響應頭,該行爲可被覆蓋。一個變通方案是單獨處理"Script error.",告知錯誤詳情僅能經過瀏覽器控制檯查看,沒法經過JavaScript訪問。例子: http://sandbox.runjs.cn/show/... 請打開頁面打開控制檯。該頁面分別加載了兩個不一樣域的js腳本,配置了crossorigin的window.onerror能夠報出詳細的錯誤,沒有配置crossorigin只能報出'script error',而且沒有錯誤信息跨域
在線上因爲JS通常都是被壓縮或者打包(webpack)過,打包後的文件只有一行,所以報錯會出現第一行第5000列出現JS錯誤,給排查帶來困難。sourceMap存儲打包前的JS文件和打包後的JS文件之間一個映射關係,能夠根據打包後的位置快速解析出對應源文件的位置。promise
可是出於安全性考慮,線上設置sourceMap會存在不安全的問題,由於網站使用者能夠輕易的看到網站源碼,此時能夠設置.map文件只能經過公司內網訪問下降隱患
sourceMap配置devtool: 'inline-source-map'
若是使用了uglifyjs-webpack-plugin 必須把 sourceMap設置爲true
https://doc.webpack-china.org...
1.6.1 sentry把全部的回調函數使用try catch封裝一層
https://github.com/getsentry/raven-js/blob/master/src/raven.js
1.6.2 vue errorHandler
https://vuejs.org/v2/api/#errorHandler
其原理也是使用try catch封裝了nextTick,$emit, watch,data等
https://github.com/vuejs/vue/blob/dev/dist/vue.runtime.js
使用window.addEventListener('error')捕獲,window.onerror捕獲不到資源加載錯誤
https://jsbin.com/rigasek/edit?html,console 圖片資源加載錯誤。此時只有window.addEventListener('error')能夠捕獲到window.onerror和window.addEventListener('error')的異同:相同點是均可以捕獲到window上的js運行時錯誤。區別是1.捕獲到的錯誤參數不一樣 2.window.addEventListener('error')能夠捕獲資源加載錯誤,可是window.onerror不能捕獲到資源加載錯誤
全部http請求都是基於xmlHttpRequest或者fetch封裝的。因此要捕獲全局的接口錯誤,方法就是封裝xmlHttpRequest或者fetch
if(!window.XMLHttpRequest) return; var xmlhttp = window.XMLHttpRequest; var _oldSend = xmlhttp.prototype.send; var _handleEvent = function (event) { if (event && event.currentTarget && event.currentTarget.status !== 200) { // 自定義錯誤上報 } } xmlhttp.prototype.send = function () { if (this['addEventListener']) { this['addEventListener']('error', _handleEvent); this['addEventListener']('load', _handleEvent); this['addEventListener']('abort', _handleEvent); } else { var _oldStateChange = this['onreadystatechange']; this['onreadystatechange'] = function (event) { if (this.readyState === 4) { _handleEvent(event); } _oldStateChange && _oldStateChange.apply(this, arguments); }; } return _oldSend.apply(this, arguments); }
if(!window.fetch) return; let _oldFetch = window.fetch; window.fetch = function () { return _oldFetch.apply(this, arguments) .then(res => { if (!res.ok) { // True if status is HTTP 2xx // 上報錯誤 } return res; }) .catch(error => { // 上報錯誤 throw error; }) }
利用以上原理,簡單寫了一個JS監控,只處理了一些JS錯誤,暫時沒有作和性能相關的監控
https://github.com/Lie8466/better-js
若是發現文章有錯誤,歡迎指正。