socket.io 在百度 App 环境下的“无妄之灾”
近期,我们收到少量用户反馈,表示产品中部分功能出现异常。经前端异常日志排查,发现问题集中于页面负责连接 Socket 的脚本文件加载失败。而相关异常日志中的 User-Agent 均包含 baiduboxapp 字样,表明问题均来自百度 App 的访问。 然而,异常日志数量较少,日均仅 100 余条,表明出现概率很低。在测试初期,一台测试手机曾短暂出现同样的现象,但随后多次刷新又恢复正常。过了一段时间,又有另一台手机能复现这个问题,于是抓紧机会进行调试。 负责连接 Socket 的脚本文件是通过动态创建 <script> 元素引入的。加载失败不外乎网络异常或脚本执行异常。通过在脚本文件开头增加调试日志,就可以确认该脚本文件是否被正常加载并开始执行。结果符合预期,日志正常地出现在 vConsole 的控制台中。不过有一条 Script error 的异常紧接着日志出现,说明是脚本执行异常。 奇怪的是,动态加载脚本时已配置 crossorigin="anonymous" 且服务端已开启跨域支持,理论上应能捕获详细错误信息,但实际却未获取到。为此,我在脚本逻辑外层包裹了 try...catch,最终捕获到具体错误信息: 并根据行列号定位到以下代码: 该代码位于 socket.io-client,它仅仅是调用全局的 addEventListener 函数注册事件回调,与 nodeType 无关。为探究根源,我在该处添加了调试日志: 最终发现,全局的 addEventListener 方法已被重写,且重写后的实现未处理 this 为 undefined 的情况(即直接调用 addEventListener 而非通过对象调用)。由于在微信、原生浏览器等环境中 addEventListener 均为原生函数,因此推断该重写行为源于百度 App。 随后我又产生了新的疑问:为何多数百度 App 用户访问正常?通过正常设备调试发现,这些设备中的 addEventListener 已经打了补丁——在重写方法中增加了 try...catch 处理。这表明大部分终端已通过热更新应用了该补丁,而少数终端可能因更新失败仍存在该问题。 针对上述情况,可采取两种解决方案。 方案一是阻止百度 App 重写 addEventListener。在页面加载初期执行以下代码,将 addEventListener 设置为不可写: 方案二是修改 socket.io-client 的代码,将调用方式由 addEventListener(...) 改为 window.addEventListener(...),确保始终通过 window 对象调用。 方案一实现简单,只需在页面中嵌入上述代码即可;方案二需修改第三方库,维护成本较高。故最终采用方案一,并在发布上线后需持续监控相关异常是否减少。排查过程

Cannot read properties of undefined (reading 'nodeType')
addEventListener(terminationEvent, unloadHandler, false);addEventListener(terminationEvent, unloadHandler, false);
console.log(addEventListener.toString());
console.log(unloadHandler.toString());

解决方案
if (/\bbaiduboxapp\b/.test(navigator.userAgent)) {
Object.defineProperty(window, 'addEventListener', {
value: window.addEventListener,
writable: false
});
}