js基础 ---- 前端如何记录浏览器报错
一、浏览器常见的几种报错分类
1、网络连接
2、http劫持
3、服务器错误
4、前端代码错误
5、前端兼容性问题
6、用户操作错误
7、跨域问题
二、如何记录这些问题
一般在本地进行调试的时候 发生错误 我们一般习惯性的先看 console 查看日志是最简单的 但是当项目上线后 这种办法基本等于无效
1、如何收集错误信息
(1)、虚拟机监控
优点:是指标齐全,并且可以进行竞品监控
缺点:是反映不全,容易失真
(2)、脚本监控
优点:是可以收集海量真实数据
缺点:是影响性能,采样少的情况下容易失真
2、常见的几种记录错误的方法
(1)、访问浏览器时间记录 performance API
在chrome浏览器控制台输入Performance.timing,会得到记录了一个浏览器访问各阶段的时间的对象。
进行错误收集的时候,可以对比这些时间,看错误发生在什么阶段
DNS 查询耗时 :domainLookupEnd - domainLookupStart
TCP 链接耗时 :connectEnd - connectStart
request 请求耗时 :responseEnd - responseStart
解析 dom 树耗时 : domComplete - domInteractive
白屏时间 :responseStart - navigationStart
domready 时间 :domContentLoadedEventEnd - navigationStart
onload 时间 :loadEventEnd – navigationStart
(2)、脚本错误收集 window.onerror
window.onerror可以捕捉运行时错误,可以拿到出错的信息,堆栈,出错的文件、行号、列号
要注意以下几点:
要把window.onerror这个代码块分离出去,并且比其他脚本先执行(注意这个前提!)即可捕捉到语法错误。
由于网络请求异常事件不会冒泡,需要在捕获阶段进行处理
不能捕获promise的错误信息
跨域资源需要专门处理,需要在script标签加上crossorigin属性,服务器设置Access-Control-Allow-Origin
window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx。
解决方案:
使用window.onerror和window.addEventListener('error')都能捕获,但是window.onerror含有详细的error堆栈信息,存在error.stack中,所以可以选择使用 onerror的方式对js运行时错误进行捕获。
window.onerror = function (msg, url, lineNo, columnNo, error) {
// 处理错误信息
}
// demo
msg: Uncaught TypeError: Uncaught ReferenceError: a is not defined
error.statck: TypeError: ReferenceError: a is not defined at http://xxxx.js:1:13
window.addEventListener('error', event => (){
// 处理错误信息
}, false);
// true代表在捕获阶段调用,false代表在冒泡阶段捕获。使用true或false都可以,默认为false
(3)、promise的错误处理
promise除了使用catch方法来捕获错误,还可以使用window的unhandledrejection事件捕获异常的
window.addEventListener("unhandledrejection", function(e){ // Event新增属性 // @prop {Promise} promise - 状态为rejected的Promise实例 // @prop {String|Object} reason - 异常信息或rejected的内容 // 会阻止异常继续抛出,不让Uncaught(in promise) Error产生 e.preventDefault() })
(4)、try catch
无法捕捉到语法错误,只能捕捉运行时错误;
可以拿到出错的信息,堆栈,出错的文件、行号、列号; 需要借助工具把所有的function块以及文件块加入try,catch,可以在这个阶段打入更多的静态信息。
要注意的是try catch只能捕获同步代码的异常,对回调,setTimeout,promise等无能为力
上报错误的方式
后端提供接口,前端ajax上传
创建一个新的图片,url参数带上错误信息
function report(error) {
var reportUrl = 'http://xxxx/report';
new Image().src = reportUrl + 'error=' + error;
}
(5)、fetch与xhr错误的捕获
对于fetch和xhr,我们需要通过改写它们的原生方法,在触发错误时进行自动化的捕获和上报。
改写fetch方法:
function _errorFetchInit () {
if(!window.fetch) return;
let _oldFetch = window.fetch;
window.fetch = function () {
return _oldFetch.apply(this, arguments)
.then(res => {
if (!res.ok) { // 当status不为2XX的时候,上报错误
}
return res;
}).catch(error => {
throw error;
})
}
}
对于XMLHttpRequest的重写:
// xhr的处理
function _errorAjaxInit () {
let protocol = window.location.protocol;
if (protocol === 'file:') return;
// 处理XMLHttpRequest
if (!window.XMLHttpRequest) {
return;
}
let xmlhttp = window.XMLHttpRequest;
// 保存原生send方法
let _oldSend = xmlhttp.prototype.send;
let _handleEvent = function (event) {
try {
if (event && event.currentTarget && event.currentTarget.status !== 200) {
// event.currentTarget 即为构建的xhr实例
// event.currentTarget.response
// event.currentTarget.responseURL || event.currentTarget.ajaxUrl
// event.currentTarget.status
// event.currentTarget.statusText
});
}
} catch (e) {va
console.log('Tool\'s error: ' + e);
}
}
xmlhttp.prototype.send = function () {
this.addEventListener('error', _handleEvent); // 失败
this.addEventListener('load', _handleEvent); // 完成
this.addEventListener('abort', _handleEvent); // 取消
return _oldSend.apply(this, arguments);
}
}
(6)、跨域错误
当网站请求并执行一个托管在第三方域名下的脚本时,就可能遇到该错误。最常见的情形是使用 CDN 托管 JS 资源
出于安全考虑,浏览器会刻意隐藏其他域的 JS 文件抛出的具体错误信息,这样做可以有效避免敏感信息无意中被不受控制的第三方脚本捕获。
因此,浏览器只允许同域下的脚本捕获具体错误信息,而其他脚本只知道发生了一个错误,但无法获知错误的具体内容。
解决:
<script src="http://another-domain.com/app.js" crossorigin="anonymous"></script>
此步骤的作用是告知浏览器以匿名方式获取目标脚本。这意味着请求脚本时不会向服务端发送潜在的用户身份信息(例如 Cookies、HTTP 证书等)。
添加跨域 HTTP 响应头:
Access-Control-Allow-Origin: * || Access-Control-Allow-Origin: http://test.com
三、Vue的错误捕捉
vue内部发生的错误会被Vue拦截,因此vue提供方法给我们处理vue组件内部发生的错误。
Vue.config.errorHandler = function (err, vm, info) {}