主要分类
- js报错
- 自定义异常
- 静态资源异常
- 接口请求异常: 业务参数错误、超时、服务器500
综合处理的异常类型
- 语法错误、代码异常
- http请求异常
- 静态资源加载异常
- Promise运行异常
- iframe异常
- 跨域scriptError
- 崩溃和卡顿
捕获异常的几种方法
1.try{}catch(e){}
可以捕获同步运行发生的错误,对异步和语法错误无能为力;
2. window.onerror
- 输出参数错误星系、错误文件、行号、列号、错误对象
- return true 防止异常向上抛出
- 须写在执行代码最前面,否则有kennel捕获不到错误
- 无法捕获语法错误、静态资源错误
- 添加script的crossorigin属性并且设置静态资源Javascript的Response为Access-Control-Allow-Origin可以捕获的js静态资源错误
3.window.addEvenListener("error", function(e) {})
- 可以捕获到资源脚本请求错误、无法判别http状态码
- 不同浏览器下的error返回的对象不同需要兼容
4.Promise catch
- 没有写catch的Promise抛出的错误无法被onerror和try-catch捕获到
- 建议使用addEvenListener("unhandlerejection", function(e {}))可以捕获没有catch的Promise的异常
5.vue react 等框架错误
Vue.config.errorHandle = function(error, vm, info) {}
React: componentDidCatch = function(error, catch) {}
**6. 页面卡顿和崩溃 **
window.load和window.beforeunload
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'));
}
- 网页崩溃无法执行window.beforeunload
基于 Service Worker 的崩溃统计方案
p1:网页加载后,通过 postMessage API 每 5s 给 sw 发送一个心跳,表示自己的在线,sw 将在线的网页登记下来,更新登记时间;
p2:网页在 beforeunload 时,通过 postMessage API 告知自己已经正常关闭,sw 将登记的网页清除;
p3:如果网页在运行的过程中 crash 了,sw 中的 running 状态将不会被清除,更新时间停留在奔溃前的最后一次心跳;
sw:Service Worker 每 10s 查看一遍登记中的网页,发现登记时间已经超出了一定时间(比如 15s)即可判定该网页 crash 了。
心跳检测机制
// 页面 JavaScript 代码
if (navigator.serviceWorker.controller !== null) {
let HEARTBEAT_INTERVAL = 5 * 1000; // 每五秒发一次心跳
let sessionId = uuid();
let heartbeat = function () {
navigator.serviceWorker.controller.postMessage({
type: 'heartbeat',
id: sessionId,
data: {} // 附加信息,如果页面 crash,上报的附加数据
});
}
window.addEventListener("beforeunload", function() {
navigator.serviceWorker.controller.postMessage({
type: 'unload',
id: sessionId
});
});
setInterval(heartbeat, HEARTBEAT_INTERVAL);
heartbeat();
}
const CHECK_CRASH_INTERVAL = 10 * 1000; // 每 10s 检查一次
const CRASH_THRESHOLD = 15 * 1000; // 15s 超过15s没有心跳则认为已经 crash
const pages = {}
let timer
function checkCrash() {
const now = Date.now()
for (var id in pages) {
let page = pages[id]
if ((now - page.t) > CRASH_THRESHOLD) {
// 上报 crash
delete pages[id]
}
}
if (Object.keys(pages).length == 0) {
clearInterval(timer)
timer = null
}
}
worker.addEventListener('message', (e) => {
const data = e.data;
if (data.type === 'heartbeat') {
pages[data.id] = {
t: Date.now()
}
if (!timer) {
timer = setInterval(function () {
checkCrash()
}, CHECK_CRASH_INTERVAL)
}
} else if (data.type === 'unload') {
delete pages[data.id]
}
})
http请求异常和数据上报
htpp接口请求异常状态码上报、或者内部约定错误的上报形式
上报信息可能过多,可以采用随机选择、或者用户特征过滤的方式
总结:
可疑区域(try-catch)
全局js异常window.error
静态资源 addEvenListener("error", function(e){})
没有catch的Promise addEvenListener("unhandlerejection", function(e){})
网页崩溃:load和beforunload|+ workService监控
http请求
跨域脚本crossorigin和Response为Access-Control-Allow-Origin
框架单独处理
async await的异常处理方式:配合使用promise返回错误和得到数据的集合,根据是否有错误来抛出异常