1. 運行時錯誤
try {
fn()
} catch (e) {
console.log(e);
}
2. 異步錯誤
try {
setTimeout(() => {
fn() // 異步錯誤
})
} catch(e) {
console.log('我感知不到錯誤');
console.log(e);
}
複製代碼
總結: 只能捕獲捉到運行時非異步錯誤,異步錯誤就顯得無能爲力,捕捉不到javascript
1.同步錯誤css
/**
* @param {String} msg 錯誤信息
* @param {String} url 出錯文件
* @param {Number} row 行號
* @param {Number} col 列號
* @param {Object} error 錯誤詳細信息
*/
window.onerror = function(msg, url, row, col, error) {
console.log('我知道錯誤了');
return true;
};
new Error();
2.異步錯誤
window.onerror = function(msg, url, row, col, error) {
console.log('我知道異步錯誤了');
return true;
};
setTimeout(() => {
new Error();;
});
複製代碼
注意:在事件處理程序中返回false,能夠阻止瀏覽器報告錯誤的默認行爲html
window.onerror = function(msg, url, row, col, error) {
return false;
}
複製代碼
當咱們遇到 "img src="./404.png" 報 404 網絡請求異常的時候,window.onerror 是沒法幫助咱們捕獲到異常的。前端
1. object.onerror
2. performance.getEntries()
3. Error
事件捕獲 (全局監控靜態資源異常)object.onerrorjava
如script,image等標籤src引用,會返回一個event
對象 ,TIPS: object.onerror
不會冒泡到window
對象,因此window.onerror沒法監控資源加載錯誤node
var img = new Image();
img.src = 'http://xxx.com/xxx.jpg';
img.onerror = function(event) {
console.log(event);
}
複製代碼
performance.getEntries()express
返回已成功加載的資源列表,而後自行作比對差集運算,覈實哪些文件沒有加載成功編程
var result = [];
window.performance.getEntries().forEach(function (perf) {
result.push({
'url': perf.name,
'entryType': perf.entryType,
'type': perf.initiatorType,
'duration(ms)': perf.duration
});
});
console.log(result);
複製代碼
**404.png**
複製代碼
window.addEventListener('error', (msg, url, row, col, error) => {
console.log('我知道 404 錯誤了');
console.log(
msg, url, row, col, error
);
return false;
}, true);
複製代碼
window.addEventListener("unhandledrejection", function(e) {
e.preventDefault()
console.log('我知道 promise 的錯誤了');
console.log(e.reason);
return true;
});
Promise.reject('promise error').catch((err)=>{
console.log(err);
})
new Promise((resolve, reject) => {
reject('promise error');
}).catch((err)=>{
console.log(err);
})
new Promise((resolve) => {
resolve();
}).then(() => {
throw 'promise error'
});
new Promise((resolve, reject) => {
reject(123);
})
複製代碼
通常涉及跨域的js運行錯誤時會拋出錯誤提示script error
,但沒有具體信息(如出錯文件,行列號提示等), 可利用資源共享策略來捕獲跨域js錯誤json
Access-Control-Allow-Origin: *
<iframe src="./iframe.html" frameborder="0"></iframe>
<script>
window.frames[0].onerror = function (msg, url, row, col, error) {
console.log('我知道 iframe 的錯誤了,也知道錯誤信息');
console.log({
msg, url, row, col, error
})
return true;
};
</script>
複製代碼
未捕獲的 JavaScript 異常一直冒泡回到事件循環時,會觸發 'uncaughtException' 事件。 默認狀況下,Node.js 經過將堆棧跟蹤打印到 stderr 並使用退出碼 1 來處理此類異常,從而覆蓋任何先前設置的 process.exitCode。 爲 'uncaughtException' 事件添加處理程序會覆蓋此默認行爲。 或者,更改 'uncaughtException' 處理程序中的 process.exitCode,這將致使進程退出並提供退出碼。 不然,在存在這樣的處理程序的狀況下,進程將以 0 退出segmentfault
process.on("uncaughtException", function(a) {
})
複製代碼
若是在事件循環的一次輪詢中,一個 Promise 被 rejected,而且此 Promise 沒有綁定錯誤處理器, 'unhandledRejection 事件會被觸發。 當使用 Promise 進行編程時,異常會以 "rejected promises" 的形式封裝。Rejection 能夠被 promise.catch() 捕獲並處理,而且在 Promise 鏈中傳播。'unhandledRejection 事件在探測和跟蹤 promise 被 rejected,而且 rejection 未被處理的場景中是頗有用的。
process.on("unhandledRejection", function(a) {
});
複製代碼
var consoleError = window.console.error;
window.console.error = function () {
alert(JSON.stringify(arguments)); // 自定義處理
consoleError && consoleError.apply(window, arguments);
};
複製代碼
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或者CSS加載以前打上時間戳,加載以後打上時間戳,而且將數據上報到後臺。加載時間反映了頁面白屏,可操做的等待時間。
<script>var cssLoadStart = +new Date</script>
<link rel="stylesheet" href="xxx.css" type="text/css" media="all">
<link rel="stylesheet" href="xxx1.css" type="text/css" media="all">
<link rel="stylesheet" href="xxx2.css" type="text/css" media="all">
<sript>
var cssLoadTime = (+new Date) - cssLoadStart;
var jsLoadStart = +new Date;
</script>
<script type="text/javascript" src="xx1.js"></script>
<script type="text/javascript" src="xx2.js"></script>
<script type="text/javascript" src="xx3.js"></script>
<script>
var jsLoadTime = (+new Date) - jsLoadStart;
var REPORT_URL = 'xxx/cgi?data='
var img = new Image;
img.onload = img.onerror = function(){
img = null;
};
img.src = REPORT_URL + cssLoadTime + '-' + jsLoadTime;
</script>
複製代碼
錯誤信息頻繁發送上報請求,會對後端服務器形成壓力。 項目中咱們可經過設置採集率,或對規定時間內數據彙總再上報,減小請求數量,從而緩解服務端壓力。
// 借鑑別人的一個例子
Reporter.send=function(data) {
// 只採集30%
if(Math.random() < 0.3) {
send(data); // 上報錯誤
}
}
複製代碼
window.onerror = function(errorMessage, scriptURI, lineNo, columnNo, error) {
// 構建錯誤對象
var errorObj = {
errorMessage: errorMessage || null,
scriptURI: scriptURI || null,
lineNo: lineNo || null,
columnNo: columnNo || null,
stack: error && error.stack ? error.stack : null
};
if (XMLHttpRequest) {
var xhr = new XMLHttpRequest();
xhr.open('post', '/middleware/errorMsg', true); // 上報給node中間層處理
xhr.setRequestHeader('Content-Type', 'application/json'); // 設置請求頭
xhr.send(JSON.stringify(errorObj)); // 發送參數
}
}
複製代碼
const express = require('express');
const fs = require('fs');
const router = express.Router();
const fetch = require('node-fetch');
const sourceMap = require('source-map');
const path = require('path');
const resolve = file => path.resolve(__dirname, file);
// 定義post接口
router.post('/errorMsg/', function(req, res) {
let error = req.body; // 獲取前端傳過來的報錯對象
let url = error.scriptURI; // 壓縮文件路徑
if (url) {
let fileUrl = url.slice(url.indexOf('client/')) + '.map'; // map文件路徑
// 解析sourceMap
let smc = new sourceMap.SourceMapConsumer(fs.readFileSync(resolve('../' + fileUrl), 'utf8')); // 返回一個promise對象
smc.then(function(result) {
// 解析原始報錯數據
let ret = result.originalPositionFor({
line: error.lineNo, // 壓縮後的行號
column: error.columnNo // 壓縮後的列號
});
let url = ''; // 上報地址
// 將異常上報至後臺
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
errorMessage: error.errorMessage, // 報錯信息
source: ret.source, // 報錯文件路徑
line: ret.line, // 報錯文件行號
column: ret.column, // 報錯文件列號
stack: error.stack // 報錯堆棧
})
}).then(function(response) {
return response.json();
}).then(function(json) {
res.json(json);
});
})
}
});
module.exports = router;
複製代碼
if (window.requestIdleCallback) {
window.requestIdleCallback()
} else {
setTimeout()
}
複製代碼
Vue.config.errorHandler = function (err, vm, info) {
let {
message, // 異常信息
name, // 異常名稱
script, // 異常腳本url
line, // 異常行號
column, // 異常列號
stack // 異常堆棧信息
} = err;
// vm爲拋出異常的 Vue 實例
// info爲 Vue 特定的錯誤信息,好比錯誤所在的生命週期鉤子
}
複製代碼
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
this.setState({ hasError: true });
// 將異常信息上報給服務器
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
return '出錯了';
}
return this.props.children;
}
}
複製代碼