记一次乌龙的排错过程

 

事情是这个样子的, 我们的项目接入了 sentry, 突然有一天, 飞书上接入的报警机器人开始疯狂报警, 错误瞬间飙升 1w 多, 虽然不影响功能, 但必须要解决啊。

sentry 上报错是这样的

Failed to execute 'dispatchEvent' on 'EventTarget': parameter 1 is not of type 'Event'.

这很明显嘛! 传入dispatchEvent的参数不是一个 Event 类型。然后想在本地复现, 因为距离报错版本并没有发新版, 所以想直接在 master 分支上复现。 乌龙问题就这样出现了。

首先本地运行 master 代码, 发现问题并没有复现, 因为认为本地代码和线上代码是一样的, 所以就很纳闷,为什么本地代码和线上打包以后的代码不一样呢? 难道打包平台会做一些其他的操作, 其实想的越多离正确答案就越远 🤣

然后开始本地代码打包, 本地启动一个服务, 发现 http 请求发不出去, 请求的接口路径用的相对路径, 开发的时候使用的代理, 所以这种方法调不通, 放弃

再想, 即然访问线上的代码报错, 那我能不能把从后端请求回来的 bundle.js 替换成本地打包的 bundle.js 呢?这样也方便排错。说干就干, 谷歌搜索大法显神通, 终于发现在 chrome 浏览器上的解决办法了

开发者工具有个 Sources 面板, 这里存放的就是从后端服务器拉取的 bundle.js, 然后找到 Overrides 面板, 这个地方就可以覆盖之前的 bundle.js

缓存过程
  1. 我们先创建一个空文件夹, 名称随便
  2. 选择 Select folder for overrides, 选择刚刚新创建的文件夹, 浏览器顶部会有个弹窗,选择允许
  3. 允许以后,这个文件夹上边有个 enable local overrides选项, 默认是选中的, 代表会重写请求, 如果有请求被重写的话, network 面板标题处会有个黄色的三角形代替
  4. 去 network 面板, 找到 bundle.js 文件, 右键, 选择 Save for overrides, bundle.js 就会被保存到第一步创建的空文件夹下了
  5. 把我们本地打包好以后的 bundle.js 替换掉第 4 步下载下来的 bundle.js
  6. 刷新页面, 就会加载本地的 bundle.js

至此, 之前的想法就实现了, 可是、可是它还是不复现 sentry 上的报错, 问题出在了哪里呢?

到此, 只能认为线上打包代码和本地代码有某些地方不一致。 现在才这么想, 不过也不晚。于是去打包平台日志面板,找到打包时的 git tag, 本地切换代码到相应的 tag, 本地打包, 重复之前的步骤, 报错复现了, 哇, 终于复现了, 老泪纵横。。。

既然问题复现了, 那么问题就好解决了, 最终定位到问题是: 我们的一个小伙伴对事件对象 e 做了一层深度复制, 如下

function test(event) {
  const e = cloneDeep(event) // 错误根本
  // ...
  const eventClone = new e.constructor(e.type, e);
  document.dispatchEvent(eventClone);
}

然后把 cloneDeep 去掉, 问题完美解决。

回想整个解错过程, 发现根本不需要如上那么复杂的步骤, 如果业务代码一致, 那么线上打包和本地开发肯定是一致的,如果不一致,那说明本地代码修改过, 这也就是一开始运行 master 代码不报错的原因, 那个 cloneDeep已经被去掉了。 一开始就陷入了错误的方向,然后越走越远。 最简单的方式应该是切换到相应的 tag, 本地跑程序,错误也会复现, 啊, 那我做了上面那么多是为了什么啊!!!

排错过程也是有收获的, 之前根本不知道 Sources 面板还有如此强大的功能。