R·ex / Zeng


音游狗、安全狗、攻城狮、业余设计师、段子手、苦学日语的少年。

从前端性能监控发现的 Chrome Bug:eval、onerror 与同源策略

背景

最近在做公司的一个前端监控项目,有一个需求是需要捕获前端错误并上报。这个需求其实很简单,使用 window.onerror 或者 window.addEventListener('error', ...) 就可以捕获异常数据,MDN 的 这篇文章 讲述了具体的用法,AlloyTeam 的 这篇文章 也讲了一些例子,以及针对触发了同源策略的应对方案。

但是当我开始写自己的库时,才发现这个需求并不是那么一帆风顺。我在我们的前端项目里引入了自己写的库,然后故意触发一个语法错误:

new ErrorWatcher(reporter);
// 下面一句会触发 Reference Error
asd;

然并卵,我获取到的错误信息只有 Script Error,就和被同源策略拦下来了一模一样。我再三确认了编译出的 bundle.jsindex.html 是同源的,并且源代码中没有任何诡异的写法。

我新写了一个空页面,尝试复现这个问题:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Normal Bundle</title>
</head>
<body>
    <script src="bundle.js"></script>
</body>
</html>
window.addEventListener('error', evt => {
    console.log(evt);
});
asd;

双击 index.html,确实是只有一个 Script Error。在高兴之余,突然想起来 file: 协议本来就不属于同源策略,于是祭出了万能的 serve -o,浏览器自动打开了 http://localhost:5000/

然后顺利的得到了 messagefilename 等信息。我特么……

解决方案

仔细查看 Console 中的日志,发现右边显示的文件地址是 webpack: 协议的,不知道是否与这个有关,于是将 Source Map 删除,发现可以正常获取信息了……Source Map 会影响运行结果?不管你信不信,反正我是不信。

于是去看了一眼编译之后的代码,发现 Dev 环境下的 bundle.js 使用了 eval-source-map,整个文件全都是通过 eval 来执行代码的。eval 可以改变内部代码的上下文,但是能不能改变内部代码的所属文件呢?于是我把自己测试用的 bundle.js 稍微改了改:

eval(`
    window.addEventListener('error', evt => {
        console.log(evt);
    });
    asd;
`);

然后 Script Error 就出现了。虽然不知道为什么,但似乎跟 eval 的关系非常大,于是我将之前项目 webpack.config.js 中的 devtooleval-source-map 改成了 source-map,问题解决了。

探索

我开始寻找为什么,因为之前搜 js script error 的结果均是清一色的同源策略相关的文章,这次知道了跟 eval 有关,于是就在谷歌中搜索 js eval onerror,发现了 Webpack 的一个 Issue,里面的链接导向了 Chromium 的这个 Issue。这是 2017 年九月份就有人提出的 Bug,但至今没有被修复。虽然该 Issue 的状态是 Assigned,但是自从被分出去之后就无人问津了。

既然这是个 Bug,那么探索就结束了。得出的一个结论:以后尽量不要用 eval,即使 Source Map 也不用。

版权声明:除文章开头有特殊声明的情况外,所有文章均可在遵从 CC BY 4.0 协议的情况下转载。

这是我们共同度过的

第 1114 天