那些年,我们解析过的前端异常
栏目: JavaScript · 发布时间: 5年前
内容简介:本文目的:能让非前端同学大致了解下,现代『前端异常解析』是怎么做的,以及大部分的坑会是哪些对于专业的前端同学,本文中也许有些坑,你还没有踩到,也可以看下。
前言
本文目的:
能让非前端同学大致了解下,现代『前端异常解析』是怎么做的,以及大部分的坑会是哪些
对于专业的前端同学,本文中也许有些坑,你还没有踩到,也可以看下。
从 window.onerror 说起
相信大部分接触过『前端错误监控』话题的同学都知道,通过浏览器提供的 window.onerror 能够捕获大部分的前端异常,比如 js 报错(本文讨论的话题),assets 加载等。
对于 js 报错,这个 api 会提供出报错消息,源文件, lineno, colno
(行号列号),详细的错误堆栈等消息,如下所示:
window.onerror = function(message, source, lineno, colno, error) { ... } 复制代码
我们拿到这些消息后就能知道,『哦,原来的我的某个 js 里的第 x 行,第 y 列,我写错了啊』。
但是,事情真的这么简单吗?
反解压缩过的 js 代码
首先第一个现实问题,我线上的 js 代码一定是经过压缩(压缩文件传输)的。所以,在 window.onerror
这个方法里拿到的 lineno 通常是 1,colno 通常是 123456 (一个很大的数字),然后当你定位到对应源码位置的时候,通常看到这类代码:
什么鬼?js 的压缩代码不仅会压缩代码行数,还会对某些可替换的临时变量名做重命名,换成简短的形式。所以正常靠肉眼要定位到真正错误的源码是很难很费劲的。
source-map 登场
这个时候我们第一个王牌登场,既然知道是怎么压缩的,那这个压缩的对应关系也一定是有的,这个对应关系就是 source-map。
然后我们通过一些反解析 source-map 的工具,把源码的 lineno, colno
(行号,列号) 和 source-map 文件输入,就能得到知道哪行源码写错了,类似效果如下:
一些安全性的考虑
通常出于安全性考虑,source-map 文件是不会随着 js 资源文件一起发布的。js 资源的发布又是很频繁的,所以会造成大量 souce-map 文件管理的问题,后面会讲到一种方案。
现在看似我们能够 cover 住所有的前端 js 异常了。但是事实上是,如果你的前端资源是通过 cdn 的方式来部署的话,你会收到很多 script error(可能 80% 都是这个错误).
script error
script error 大部分是浏览器跨域 安全限制导致的。通常的解决方式是,对跨域的脚本加上 crossorigin 属性就好了,但还有一个必要条件是服务端要支持这个属性。这个服务端包括:
- cdn 的服务器。
- 低端的安卓系统版本(离线包拦截请求无法设置这个属性)。
所以在开启这个属性前,一定要确认好以上两个地方是否支持,不然的话,嘿嘿,白屏!
ok,到现在我们基本解决 window.onerror 报上来的问题了。
那还有其他的前端异常吗?
成也框架,败也框架
近些年,随着前端工程化,组件化的流行,越来越多的 MVVM 的框架登上历史舞台(vue,react,angular 等)。
对前端监控来说,拿 vue 来举例。
你写的 vue 业务代码,会发现 window.onerror 是监控不到的。原因是这些框架他们内部会消化掉这些错误,必须得通过他们的 API 才能 catch 到。比如:
Vue.config.errorHandler = function (err, vm, info) { // handle error // `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子 } 复制代码
重点注意一下,这个函数不像 window.onerror
会把错误的 lineno, colno
一起上报。只能看到一个干巴巴的错误消息,比如:
TypeError: Cannot read property 'id' of null at a.next (https://xxx.com/0-1.0.0-5ba93bf.js:1:25746) at n (https://xxx.com/x2-1.0.0-5ba93bf.js:13:1417) 复制代码
这是线上代码,同样也是经过压缩的,而且由于被框架代码包了一层,在压缩过的代码里定位变得更加费劲。
那有什么方法可以拿到 lineno, colno
然后通过 source-map 反解析源码么?
答案其实就在上面,在错误堆栈里。仔细看 https://xxx.com/0-1.0.0-5ba93bf.js:1:25746
,按照 js stack 的生成规则,通常 *.js:x:y
第一个冒号后面的 x 代表了 lineno
行数,第二个冒号后面的 y 代表了 colno
列数。
所以我们通过肉眼或者是可以正则解析出 lineno, colno
的,然后通过 source-map 工具能够定位出具体的 vue 代码出错位置了。
同样要问一个,那还有其他的前端异常了吗?
明明白屏了,咋不报错呢
假设不小心,写了如下的 vue 代码。
<template> </template> <script> export default { mounted () { const somePromise = new Promise(_ => { let a a.doingNothing // 空指针,出错啦!!! }) somePromise.then(console.log) } } </script> 复制代码
明明里面有一个空指针会导致白屏的问题,可是你会发现 window.onerror
捕捉不到, Vue.config.errorHandler
同样也捕捉不到(不是说好框架全吃的吗!)。
仔细看,哦,原来这是写在 promise 里的代码啊,这个代码被这个 promise 吃掉了,因为这里没有 catch,所以也就不知道了。
随着 js 语言的发展,越来越多的特性被引入到浏览器中,按照新的规范,像 promise 里面的错误应该 promise 由开发者自己负责来 catch 而不会被 window.onerror 捕获。
但是理想很丰满,现实却还是 ---- 不能保证每个人都会写 catch。
所以对于 promise 的错误,我们需要做全局监听,代码如下:
window.addEventListener('unhandledrejection', function (event) { const error = event && event.reason console && console.warn && console.warn('WARNING: Unhandled promise rejection. Shame on you! Reason: ' + error) // ... report error } 复制代码
同样对于这里没有上报 lineno, colno
,但是有 error stack,我们可以使用同样的套路对他们用 source-map 来反解析。
以上所述就是小编给大家介绍的《那些年,我们解析过的前端异常》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 前端路由原理解析和实现
- 前端路由原理解析和实现
- 5000字解析:前端五种跨平台技术
- 可能是最全面最易懂的解析前端浮动的文章
- 高级前端进阶:手把手教你实现一个 AST 解析器
- 深入解析 Node.js 的 console.log[每日前端夜话0x73]
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Elements of Information Theory
Thomas M. Cover、Joy A. Thomas / Wiley-Blackwell / 2006-7 / GBP 76.50
The latest edition of this classic is updated with new problem sets and material The Second Edition of this fundamental textbook maintains the book's tradition of clear, thought-provoking instr......一起来看看 《Elements of Information Theory》 这本书的介绍吧!