1、爲何要處理異常?
-
加強用戶體驗; -
遠程定位問題; -
未雨綢繆,及早發現問題; -
沒法複線問題,尤爲是移動端,機型,系統都是問題; -
完善的前端方案,前端監控系統;
2、須要處理哪些異常?
-
JS 語法錯誤、代碼異常 -
AJAX 請求異常 -
靜態資源加載異常 -
Promise 異常 -
Iframe 異常 -
跨域 Script error -
崩潰和卡頓
3、Try-Catch 的誤區
-
同步運行時錯誤:
try {
let name = 'jartto';
console.log(nam);
} catch(e) {
console.log( '捕獲到異常:',e);
}
捕獲到異常: ReferenceError: nam is not defined
at <anonymous>: 3: 15
-
不能捕獲到具體的語法錯誤,只有一個語法錯誤提示。咱們修改一下代碼,刪掉一個單引號:
try {
let name = 'jartto;
console.log(nam);
} catch(e) {
console.log('捕獲到異常: ',e);
}
Uncaught SyntaxError: Invalid or unexpected token
不過語法錯誤在咱們開發階段就能夠看到,應該不會順利上到線上環境。
-
異步錯誤
try {
setTimeout( () => {
undefined.map( v => v);
}, 1000)
} catch(e) {
console.log( '捕獲到異常:',e);
}
Uncaught TypeError: Cannot read property 'map' of undefined
at setTimeout ( <anonymous>:3:11)
4、window.onerror 不是萬能的
/**
* @param {String} message 錯誤信息
* @param {String} source 出錯文件
* @param {Number} lineno 行號
* @param {Number} colno 列號
* @param {Object} error Error對象(對象)
*/
window.onerror = function(message, source, lineno, colno, error) {
console.log( '捕獲到異常:',{message, source, lineno, colno, error});
}
-
首先試試同步運行時錯誤
window.onerror = function(message, source, lineno, colno, error) {
// message:錯誤信息(字符串)。
// source:發生錯誤的腳本URL(字符串)
// lineno:發生錯誤的行號(數字)
// colno:發生錯誤的列號(數字)
// error:Error對象(對象)
console.log( '捕獲到異常:',{message, source, lineno, colno, error});
}
Jartto;
![](http://static.javashuo.com/static/loading.gif)
-
再試試語法錯誤呢?
window.onerror = function(message, source, lineno, colno, error) {
console.log( '捕獲到異常:',{message, source, lineno, colno, error});
}
let name = 'Jartto
Uncaught SyntaxError: Invalid or unexpected token
-
懷着忐忑的心,咱們最後來試試異步運行時錯誤:
window.onerror = function(message, source, lineno, colno, error) {
console.log( '捕獲到異常:',{message, source, lineno, colno, error});
}
setTimeout( () => {
Jartto;
});
捕獲到異常:{ message: "Uncaught ReferenceError: Jartto is not defined", source: "http://127.0.0.1:8001/", lineno: 36, colno: 5, error: ReferenceError: Jartto is not defined
at setTimeout (http: //127.0.0.1:8001/:36:5)}
-
接着,咱們試試網絡請求異常的狀況:
<script>
window.onerror = function(message, source, lineno, colno, error) {
console.log( '捕獲到異常:',{message, source, lineno, colno, error});
return true;
}
< /script>
<img src="./jartto.png ">
咱們發現,不管是靜態資源異常,或者接口異常,錯誤都沒法捕獲到。
window.onerror = function(message, source, lineno, colno, error) {
console.log( '捕獲到異常:',{message, source, lineno, colno, error});
return true;
}
setTimeout( () => {
Jartto;
});
Uncaught ReferenceError: Jartto is not defined
at setTimeout ((index): 36)
-
onerror 最好寫在全部 JS 腳本的前面,不然有可能捕獲不到錯誤; -
onerror 沒法捕獲語法錯誤;
5、window.addEventListener
<scritp>
window.addEventListener( 'error', (error) => {
console.log( '捕獲到異常:', error);
}, true)
< /script>
<img src="./jartto.png ">
![](http://static.javashuo.com/static/loading.gif)
-
不一樣瀏覽器下返回的 error 對象可能不一樣,須要注意兼容處理。 -
須要注意避免 addEventListener 重複監聽。
6、Promise Catch
window.addEventListener( "unhandledrejection", function(e){
console.log(e);
});
window.addEventListener( "unhandledrejection", function(e){
e.preventDefault()
console.log( '捕獲到異常:', e);
return true;
});
Promise.reject( 'promise error');
![](http://static.javashuo.com/static/loading.gif)
window.addEventListener( "unhandledrejection", function(e){
e.preventDefault()
console.log( '捕獲到異常:', e);
return true;
});
new Promise( (resolve, reject) => {
reject( 'jartto: promise error');
});
event.preventDefault();
VUE errorHandler
Vue.config.errorHandler = (err, vm, info) => {
console.error( '經過vue errorHandler捕獲的錯誤');
console.error(err);
console.error(vm);
console.error(info);
}
componentDidCatch(error, info) {
console.log(error, info);
}
-
事件處理器 -
異步代碼 -
服務端的渲染代碼 -
在 error boundaries 區域內的錯誤
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}
render() {
if ( this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
9、iframe 異常
window.onerror = function(message, source, lineno, colno, error) {
console.log( '捕獲到異常:',{message, source, lineno, colno, error});
}
<iframe src= "./iframe.html" frameborder= "0"> </iframe>
<script>
window.frames[0].onerror = function (message, source, lineno, colno, error) {
console.log('捕獲到 iframe 異常:',{message, source, lineno, colno, error});
return true;
};
</script>
10、Script error
<script src= "http://jartto.wang/main.js" crossorigin> </script>
const script = document.createElement( 'script');
script.crossOrigin = 'anonymous';
script.src = url;
document.body.appendChild(script);
const originAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (type, listener, options) {
const wrappedListener = function (...args) {
try {
return listener.apply( this, args);
}
catch (err) {
throw err;
}
}
return originAddEventListener.call( this, type, wrappedListener, options);
}
( () => {
const originAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (type, listener, options) {
+ // 捕獲添加事件時的堆棧
+ const addStack = new Error( `Event (${type})`).stack;
const wrappedListener = function (...args) {
try {
return listener.apply( this, args);
}
catch (err) {
+ // 異常發生時,擴展堆棧
+ err.stack += '\n' + addStack;
throw err;
}
}
return originAddEventListener.call( this, type, wrappedListener, options);
}
})();
11、崩潰和卡頓
-
利用 window 對象的 load 和 beforeunload 事件實現了網頁崩潰的監控。不錯的文章,推薦閱讀:Logging Information on Browser Crashes。
window.addEventListener( 'load', function () {
sessionStorage.setItem( 'good_exit', 'pending');
setInterval( function () {
sessionStorage.setItem( 'time_before_crash', new Date().toString());
}, 1000);
});
window.addEventListener( 'beforeunload', function () {
sessionStorage.setItem( 'good_exit', 'true');
});
if(sessionStorage.getItem( 'good_exit') &&
sessionStorage.getItem( 'good_exit') !== 'true') {
/*
insert crash logging code here
*/
alert( 'Hey, welcome back from your crash, looks like you crashed on: ' + sessionStorage.getItem( 'time_before_crash'));
}
-
基於如下緣由,咱們可使用 Service Worker 來實現網頁崩潰的監控:
12、錯誤上報
function report(error) {
let reportUrl = 'http://jartto.wang/report';
new Image().src = `${reportUrl}?logs=${error}`;
}
Reporter.send = function(data) {
// 只採集 30%
if( Math.random() < 0.3) {
send(data) // 上報錯誤信息
}
}
十3、總結
-
可疑區域增長 Try-Catch -
全局監控 JS 異常 window.onerror -
全局監控靜態資源異常 window.addEventListener -
捕獲沒有 Catch 的 Promise 異常:unhandledrejection -
VUE errorHandler 和 React componentDidCatch -
監控網頁崩潰:window 對象的 load 和 beforeunload -
跨域 crossOrigin 解決
十4、參考
-
Logging Information on Browser Crashes http://jasonjl.me/blog/2015/06/21/taking-action-on-browser-crashes/ -
前端代碼異常監控實戰 https://github.com/happylindz/blog/issues/5 -
Error Boundaries https://blog.csdn.net/a986597353/article/details/78469979 -
前端監控知識點 https://github.com/RicardoCao-Biker/Front-End-Monitoring/blob/master/BasicKnowledge.md -
Capture and report JavaScript errors with window.onerror https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror
做者: Jartto's blogjavascript
http://jartto.wang/2018/11/20/js-exception-handling/html
掃碼關注,精彩內容第一時間推給你前端
-- 前端技術江湖 -- vue
本文分享自微信公衆號 - 前端技術江湖(bigerfe)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。java