如何完成一个记录网站加载脚本时发生的错误,并把错误上报呢?这是网站上线时最经常的需求。
一般当我们的脚本发生错误时,浏览器都会在console里体现出错误信息,并且会提示我们出错的文件,行号,堆栈信息,此时js停止往下执行:比如这样:
到这里先问自己一个问题,前端异常具体是指什么呢?
第一种情况:JS脚本里边存着语法错误,第二种情况:JS脚本在运行时发生错误
一般有两种情况可以处理这两种错误:
一种是try,catch方案,我们针对性的在可能出错的地方使用try,catch块。这个代码块如果出错了我们可以在catch块中收集并处理。
一种是监听window的onerror事件,比如我们使用window.onerror=function(){}
或者是window.addEventListener("error",function(event){})
,这个onerror可以收集语法错误和运行时的错误,还可以知道出错的信息,文件,行号,列号等。
利用try,catch的方法
先来回忆复习下try,catch
的基本用法。下面是我们最初学try
,catch时会写的一个小demo。主要是throw
的用法。throw
语句允许我们创建自定义错误。正确的技术术语是:创建或抛出异常(exception)。如果把 throw
与 try
和 catch
一起使用,就能够更加精准控制程序流,并生成自定义的错误消息。throw exception
,这个异常可以是我们创建的字符串,数字,逻辑值,对象等。注意要小写。
1 | <input type="text" placeholder="请填入一个5到10之间的数字" id="inputValue"> |
1 | function test(){ |
但是,try,catch有两个很严重的问题,第一个就是如果代码块有语法错误,此时JS解释器不会执行当前这块代码块,也更加不会执行catch里面的东西了。第二个问题就是try,catch可以理解成一个函数块,这有这个里面的运行错误才会被捕捉,也就是没有办法捕捉到全局错误事件。
第一个就是语法错误,不会捕捉,会停止运行,上代码:
1 | try{ |
如图所示,并不会打印出错误信息,只是在控制台直接输出了错误信息。如果我们把这个语法错误变为执行时的错误,就是可以捕捉到的。如下图所示:
1 | try{ |
第二种情况怎么理解呢?
try
,catch
只能捕捉到当前执行流里面的运行错误,对于异步回调来说,是不能被try
,catch
来捕捉的。我们知道js是单线程的,回调里面的都被放到了任务队列里面。如下例子:1
2
3
4
5
6
7
8try{
var btn = document.getElementsByTagName("button")[0];
btn.onclick = function(){
i<l;
}
}catch(e){
console.log(e);
}
这时候我们打开浏览器是不会有任何东西输出的。
利用window.onerror方法
先具体看下onerror在什么情况下会被触发:An event handler for the error event. Error events are fired at various targets for different kinds of errors:
When a JavaScript runtime error (including syntax errors) occurs, an error event using interface ErrorEvent is fired at window and window.onerror() is invoked.
When a resource (such as an
<img>
or<script>
) fails to load, an error event using interface Event is fired at the element, that initiated the load, and the onerror() handler on the element is invoked. These error events do not bubble up to window, but (at least in Firefox) can be handled with a single capturing window.addEventListener.
具体的参数和语法如下,可以去MDN上看.
比如我们就把onerror
的arguments对象打印出来就可以得到下面的结果:
或者可以获得到出错信息的文件名,行号,列号,并且通过设置返回值为true或者false来保证信息不输出到控制台上。比如下面的例子:1
2
3
4
5
6
7
8
9
10
11
12window.onerror = function(message, source, lineno, colno, error) {
console.log("-----------");
console.log(message);
console.log(source);
console.log(lineno);
console.log(colno);
console.log(error);
return true;
}
//line1
if = 1;
会得到下面的结果:
这里注意下,语法错误不能放在onerror
代码块内部,如果放在里面,因为语法在这个onerror
里面都没有被通过,就更不可能会被执行了,就会跟平常一样代码直接打印到控制台。所以我们要把onerror
单独提取出来,并放在其他脚本前面执行。
这里还要注意一点,就是如果我们的js文件不是本地文件,是跨域文件,当这个文件出现了错误时,我们是不能够获得详细的信息的。只会简单报错: Script error
,这样是为了防止信息泄露,避免不安全。那如果我们确实要跨域访问,怎么办呢?
我们需要在客户端和服务器端同时设置可跨域访问。客户端中的image
和script
标签都有 crossorigin
参数,这个属性是告诉浏览器我要加载的这个资源是可以信任的。并在服务器端再设置:Access-Control-Allow-Origin
的响应头。比如header('Access-Control-Allow-Origin: *');
。下面这段话仔细看下可能更加好理解。
When a syntax(?) error occurs in a script, loaded from a different origin, the details of the syntax error are not reported to prevent leaking information (see bug 363897). Instead the error reported is simply “Script error.” This behavior can be overriden in some browsers using the crossorigin attribute on
<script>
and having the server send the appropriate CORS HTTP response headers.
解决方案
在解决具体问题前,我们来看下window.onerror
里面的第五个参数,error,这个信息。它看起来并不是那么特别。因为它里面包含几个例如:message, fileName, and lineNumber。这些事已经直接在前面参数提供给了你的。但是这个里面有一个非标准的属性,Error.prototype.stack
。这个是追踪错误信息的关键所在。而且虽然不是标准的,但是是在主流浏览器中都支持的。这个error对象是会发生变化的,比如如果是语法错误,就会显示的是下面的SyntaxError,如果是未定义错误,会是ReferenceError。打印下error.stack
。
这个里面的stack属性,就是可以追踪的所在,我们这里的错误比较简单,所以是只有一层。
如果是比较复杂的,打印出来可能是类似这样的。
Error: foobar
at bar (Unknown script code:2:5)
at foo (Unknown script code:6:5)
at Anonymous function (Unknown script code:11:5)
at Anonymous function (Unknown script code:10:2)
at Anonymous function (Unknown script code:1:73)
这里具体的代码我在网上看到有人写了两种,我觉得两种都有自己的优缺点。学习了。
第一个来自知乎的水歌,链接为:水歌的回答。学习了。
1 | (function (BOM, $) { |
这段代码是只追踪了一层stack,并提交给了server。看到下面的post和get方法分别提交了吗?这个作者的回答是:
如果有详细的调用堆栈信息,就用 POST 这种理论上无限容量的方法发送,否则就只把 错误简介、文件名、出错行号 等信息用 GET 发送,以提高网络传输、服务器端解析的性能~
第二种写法来自rapheal的博客,rapheal的这种写法,是追踪了3层错误信息。学习了。
1 | window.onerror = function(msg,url,line,col,error){ |
涨知识
顺便提到这里,最近被问到了一个问题,怎么测试网页前端性能。我以前有用过webpagetest这个网站在线测试过我的网站的性能。但是当时并没有认真的去分析到底里面有些什么使网站的加载变得慢,或者哪些是可以优化的地方。反思。最近我又看了看,这个真的是很好。仔细看的话,可以详细掌握网站加载过程中的瀑布流、性能得分、元素分布、视图分析等数据。其中比较直观的视图分析功能可以直接看到页面加载各个阶段的截屏。还可以看到:白屏时间和首屏时间,即用户多久能在页面中看到内容,以及多久首屏渲染完成(包含图片等元素加载完成)。这两个时间点直接决定了用户需要等待多久才能看到自己想看到的信息。谷歌优化建议中也提到减少非首屏使用的 css 及 JS,尽快让首屏呈现。回头我专门再研究下,再写一篇博客。
总结
主要是学习了两种追踪错误的写法,一种是try,catch。一种是onerror。主要学习自下面这几篇文章。推荐阅读: