静态资源加载的错误处理
图片资源加载的错误处理
当一项资源(如img或script)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window,不过能被单一的window.addEventListener捕获。
图片也支持 error 事件,可以使用 addEventListener 或者 onerror 来添加。
1 | var image = new Image(); |
1 | var image = new Image(); |
window.onerror 和 window.addListener(‘error’, () => {}) 都可以捕获到js的错误
区别是:
1.捕获到的错误参数不同
2.window.addEventListener(‘error’)可以捕获资源加载错误,但是window.onerror不能捕获到资源加载错误,window.addEventListener(‘error’)捕获到的错误,可以通过target?.src || target?.href区分是资源加载错误还是js运行时错误
script资源加载的错误处理
script资源加载与图片等其他资源加载没有太大区别
但是有需要注意的地方:
当加载自不同域的脚本中发生语法错误时,为避免信息泄露(参见bug 363897),语法错误的细节将不会报告,而代之简单的”Script error.”。在某些浏览器中,通过在script使用crossorigin属性并要求服务器发送适当的 CORS HTTP 响应头,该行为可被覆盖。一个变通方案是单独处理”Script error.”,告知错误详情仅能通过浏览器控制台查看,无法通过JavaScript访问。
通过script加载跨域脚本时,脚本如果报错的情况,报错信息会缺少 stack
信息,error显示为null,并且错误事件中的 message、lineno、filename 显示是不正确的
这里说的“看不到stack和正确的错误信息”是指使用 onerror 或者 addEventListener 捕获的错误对象
如果想知道详细信息还是可以通过打开控制台,查看error窗口来获取真正的信息
如果想要获取正确的信息,则加载跨域脚本时,需要添加 crossorigin
属性,并且资源的请求头需要带上 Access-Control-Allow-Origin: http://www.yourdomain.com
错误事件中的 message、lineno、filename 会显示正确
通用的处理方式,参考MDN
1 | window.onerror = function (msg, url, lineNo, columnNo, error) { |
Promise异常全局处理
1 | Promise.resolve().then(() => console.log(c)); |
当promise被reject并且错误信息没有被处理的时候,会抛出一个unhandledrejection,并且这个错误不会被window.onerror以及window.addEventListener(‘error’)捕获。
1 | window.addEventListener("unhandledrejection", event => { |
如果需要在全局捕获unhandledrejection,需要用专门的window.addEventListener(‘unhandledrejection’)捕获处理。
使用 event.preventDefault()
可以阻止在开控制台输出默认的错误信息。
使用 window.onunhandledrejection
也可以实现同样的效果
1 | window.onunhandledrejection = event => { |
Ajax请求异常
XMLHttpRequest
举一个简单的例子:
1 | var xhr = new XMLHttpRequest(); |
需要注意的时:XMLHttpRequest的error事件与http status无关,只有在请求过程中,发生 Network error(网络错误)才会触发此事件,比如:
1、请求过程中,网络中断
2、断网环境
对于应用层级别的异常,如响应返回的xhr.statusCode是 4xx 或 5xx 时,并不属于 Network error ,所以不会触发 onerror 事件,而是会触发 onload 事件。
Fetch
举个简单的例子:
1 | fetch('http://127.0.0.1:8080/index.error') |
fetch 与 XMLHttpRequest 捕获异常上不同的点在于:
fetch 可以 catch 应用层的异常,比如:4xx,5xx
常见错误与异常
EvalError
创建一个error实例,表示错误的原因:与 eval() 有关。
EvalError 不在当前ECMAScript规范中使用,因此不会被运行时抛出. 但是对象本身仍然与规范的早期版本向后兼容.
常见于:
1、给 eval 赋值
1 | eval = ''; |
但是现代引擎有可能会允许这种情况,比如:chrome下会出报错
2、eval在非函数调用的情况下被使用
1 | new eval(); |
但是现代引擎有可能不会报 EvalError,如果满足其他错误的场景会优先展示别的错误类型
1 | try { |
InternalError(非Web标准的错误类型,目前只有Firefox实现了)
创建一个代表Javascript引擎内部错误的异常抛出的实例。 如: “递归太多”。
RangeError
表示错误的原因:数值变量或参数超出其有效范围。
常见于:
1、创建一个负长度数组
2、Number 对象的方法参数超出范围
ReferenceError
表示错误的原因:无效引用。
常见的错误提示:xxxx is not defined
常见于:
1、引用了一个不存在变量
1 | try { |
2、主动抛出一个引用错误
1 | try { |
SyntaxError
JS 引擎解析 js 代码块时,先进行词法语法分析,若发现不符合语法规范的 token 或 token 顺序时抛出 SyntaxError(语法错误),会导致整个 js 文件无法执行。
比如:Chrome中会报”Uncaught SyntaxError: Unexpected token”
常见于几种场景:
1、JS资源文件下载失败
2、JS中使用了当前引擎不支持的语法特性
1 | try { |
TypeError
表示错误的原因:变量或参数不属于有效类型;对象用来表示值的类型发生了非预期类型时的操作。
常见于:
1、基础类型的值用于方法调用
1 | var foo = 1; |
1 | try { |
URIError
用来表示以一种错误的方式使用全局URI处理函数而产生的错误。
常见于:
1、使用decodeURIComponent时,传入了错误格式的url,其他的URL操作方法也会出现这种错误,比如:decodeURI,encodeURL等
1 | decodeURIComponent('%'); |
1 | try { |
自定义错误
1 | class MyError extends Error { |
错误处理
局部处理
常用的处理方式使用 try catch
语句包含执行的代码块
1 | try { |
全局处理
任何没有被 try/catch 语句处理的错误都会在 window 对象上触发 error 事件。当JavaScript运行时错误(包括语法错误)发生时,window会触发一个ErrorEvent接口的error事件,并执行window.onerror()。
触发 error 事件会传入 3 个参数:错误消息、发生错误的 URL 和行号
1 | window.onerror = (message, url, lineNumber) => { |
浏览器在使用这个事件处理错误时存在明显差异。在IE中发生error事件时,正 常代码会继续执行,所有变量和数据会保持,且可以在 onerror 事件处理程序中访问。 然而在 Firefox 中,正常代码会执行会终止,错误发生之前的所有变量和数据会被销毁, 导致很难真正分析处理错误。
结论
- 使用window.onerror 或 window.addEventListener(‘error’) 捕获JS运行时错误
- 使用window.onunhandledrejection 或 window.addEventListener(‘unhandledrejection’)捕获未处理的promise reject错误
- 在跨域脚本上配置
crossorigin="anonymous"
,并且配置资源的Access-Control-Allow-Origin: http://www.yourdomain.com
来正确捕获跨域脚本错误 - 使用window.addEventListener(‘error’)捕获资源加载错误。因为它也能捕获js运行时错误,为避免重复上报js运行时错误,此时只有event.srcElement inatanceof HTMLScriptElement或HTMLLinkElement或HTMLImageElement时才上报
- XMLHttpRequest 使用 addEventListener(‘error’) 来处理异常
- fetch 使用通常的 Promise catch 的方式即可,try catch 也可以