前端的异常场景

  • Javascript 运行错误
  • 异步异常
  • 网络加载错误
  • HTTP 请求错误

Javascript 运行错误

  • EvalError:在 eval() 函数中发送的错误
  • InternalError:内部错误
    • 比如递归太深,导致卡死
  • RangeError:超出数字范围的错误
    • 比如数组越界
  • ReferenceError:非法引用
    • 变量未定义
  • SyntaxError:语法错误
  • TypeError:类型错误
  • URIError:在 encodeURI() 中发生的错误

异步异常

  • setTimeout
  • setInterval
  • Promise
  • requestAnimationFrame

    捕获异常需要在内部加才能生效。

image.png

网络加载错误

  • link 标签
  • script 标签
  • img 标签
  • css 样式表

    HTTP 请求错误

  • XMLHTTPRequest

  • fetch

    400、500 等状态码的错误

异常的捕获

try catch

  1. try {
  2. var name = 55
  3. console.log(anme)
  4. } catch(err) {
  5. console.log(err);
  6. }

window.onerror 全局捕获

window.onerrorwindow.addEventListener('error', () => { })
两者相同点

  • 都能捕获代码运行时的错误。

后者可以捕获网络加载错误,前者不能

  1. window.onerror = (message, source, lineno, colno, error) => {
  2. console.log(message);// 错误信息
  3. console.log(source); // 源文件 url 路径
  4. console.log(lineno); // 第几行
  5. console.log(colno); // 第几列
  6. console.log(error); // 错误信息,以及出错代码的 url 连接
  7. }
  8. window.addEventListener('error', (errorEvent) => {
  9. const { message, filename, lineno, colno, error } = errorEvent
  10. console.log(message);
  11. console.log(filename)
  12. console.log(lineno)
  13. console.log(colno);
  14. console.log(error);
  15. })
  16. console.log(asdf)

image.png

捕获 promise 异常

  1. window.addEventListener('unhandledrejection', function (event) {
  2. // 处理未处理的拒绝
  3. // 防止默认错处(例如将错误输出到控制台)
  4. event.preventDefault();
  5. })

unhandledrejection 的兼容性

image.png

跨域 script error

浏览器只允许同域下的脚本捕获具体的错误信息。
解决方法有两种

  1. script 脚本设置crossorigin="anonymous",并且服务器响应头设置Access-Control-Origin: *(或者当前域名)

image.png

服务端响应头Access-Control-Origin: *(或者当前域名)

  1. 重写浏览器addEventListener方法 ```javascript const orignAddEventListener = EventTarget.prototype.addEventListener;

EventTarget.prototype.addEventListener = function (type, listener, options) { const wrappedListener = function (…args) { try { return listener.apply(this, args); } catch (error) { throw err } } return orignAddEventListener.call(this, type, wrappedListener, options) }

  1. <a name="XuSIg"></a>
  2. ## Vue error handler
  3. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/1174243/1645454032561-7254aac0-b0f5-4795-b3f8-4afe828d60a0.png#clientId=u31ea2781-9f7f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=226&id=udd5f6114&margin=%5Bobject%20Object%5D&name=image.png&originHeight=282&originWidth=866&originalType=binary&ratio=1&rotation=0&showTitle=false&size=78516&status=done&style=none&taskId=ud3d08e6b-6d01-4432-ab68-a2242b35650&title=&width=692.8)
  4. <a name="dt57T"></a>
  5. ## HTTP 请求错误
  6. 重写 fetch 和 XMLHttpRequest<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/1174243/1645454095240-b517e46f-497a-4fb5-89de-02fb23709632.png#clientId=u31ea2781-9f7f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=526&id=u8d961bea&margin=%5Bobject%20Object%5D&name=image.png&originHeight=658&originWidth=865&originalType=binary&ratio=1&rotation=0&showTitle=false&size=187465&status=done&style=none&taskId=u337a86be-c705-4289-aba0-96877cf7a15&title=&width=692)
  7. <a name="nvIVb"></a>
  8. ## 网页崩溃
  9. 使用 Server worker 来监控。
  10. <a name="vUAPH"></a>
  11. # 错误上报
  12. - XMLHttpRequest
  13. - navigator.sendBeacon
  14. - IndexedDB 缓存,异步上传
  15. - 页面截图(html2canvas)
  16. <a name="uL7GZ"></a>
  17. # SourceMap
  18. sourceMap 文件
  19. ```json
  20. {
  21. version : 3,
  22. file: "out.js",
  23. sourceRoot : "",
  24. sources: ["foo.js", "bar.js"],
  25. names: ["src", "maps", "are", "fun"],
  26. mappings: "AAgBC,SAAQ,CAAEA"
  27. }

image.png
image.png
image.png

错误堆栈查看工具

堆栈解析工具:https://github.com/stacktracejs/error-stack-parser

UglifyJS 反向美化

  1. const UglifyJS = require('uglify-js');
  2. const fs = require('fs');
  3. const path = require('path');
  4. const sourceMap = require('source-map');
  5. const source = fs.readFileSync(path.join(__dirname, './promise.js')).toString()
  6. const result = UglifyJS.minify(source, {
  7. output: {
  8. beautify: true
  9. },
  10. sourceMap: {
  11. filename: 'promise.js',
  12. url: 'promise.js.map'
  13. }
  14. })
  15. const code = result.code;
  16. const rawSourceMap = JSON.parse(result.map);
  17. const consumerPromise = new sourceMap.SourceMapConsumer(rawSourceMap);
  18. consumerPromise.then(consumer => ({
  19. code: code,
  20. sourceMapConsumer: consumer
  21. })).then(result => {
  22. console.log(result)
  23. })

vscode 使用的编辑器:https://github.com/microsoft/monaco-editor

Sentry

image.png

参考资料

《JavaScript Source Map 详解》
《从 0 到 1 搭建前端监控系统之异常监控》
《Source Map》