重写 window.websocket 是最直接有效的全局钩子方式;需缓存原生构造函数、手动挂载静态属性、采用“代理方法+事件中转”双层拦截,避免 proxy 方案及递归调用,复杂业务宜封装拦截器链。

重写 window.WebSocket 是最直接有效的全局钩子方式
浏览器中没有原生的 WebSocket 全局拦截 API,window.WebSocket 构造函数是唯一可稳定劫持的入口点。只要在页面脚本执行前注入重写逻辑(例如通过 DevTools Console、油猴脚本或代理注入),后续所有 new WebSocket(...) 调用都会走你的代理逻辑。
关键点在于:必须先缓存原生构造函数,再覆盖;否则会递归调用自身导致栈溢出。常见错误是漏掉 protocols 参数分支,或未处理 WebSocket.CLOSED 等静态属性——这些不会被代理实例继承,需手动复制。
- 务必用
const NativeWebSocket = window.WebSocket缓存,不能用self.WebSocket或this.WebSocket - 若目标页面使用了
WebSocket.CONNECTING等常量,需在重写后手动挂载:window.WebSocket.CONNECTING = NativeWebSocket.CONNECTING - 不要在构造函数内直接监听
message事件并修改event.data——该属性只读,强行赋值无效;应改用事件委托或包装回调
send() 和 onmessage 的拦截必须分层处理
单纯重写 ws.send 方法只能捕获发送动作,但无法统一处理序列化、鉴权头、重试等逻辑;而直接覆写 ws.onmessage 又会丢失原有业务监听器。正确做法是“代理方法 + 事件中转”双层拦截。
示例中常见陷阱:把 originalSend.call(ws, data) 写成 ws.send(data),这会导致无限递归;或在 addEventListener('message', ...) 中直接 console.log(event.data) 就结束,没把事件继续派发给原始监听器。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 发送拦截:用闭包保存原始
send,在包装函数中做日志、加签、JSON.stringify 等,最后调用原始方法 - 接收拦截:用
ws.addEventListener('message', handler)拦截原始事件,处理完后手动触发自定义事件(如dispatchEvent(new CustomEvent('ws:message', { detail: parsed }))),让业务侧监听新事件 - 避免覆盖
ws.onopen等属性式事件处理器,它们和addEventListener不共存;统一用后者注册更可靠
Proxy 方案在 Chrome 120+ 后变得不稳定
早期用 new Proxy(ws, { get }) 劫持 send 看似优雅,但现代浏览器(尤其是 Chrome 120+)对 WebSocket 实例的内部属性做了更严格的访问控制。Proxy 无法正确代理 bufferedAmount、readyState 等 getter,且部分版本会触发 Illegal invocation 错误。
实测发现:Firefox 对 Proxy 支持稍好,但 Safari 会静默失败;所有浏览器均不保证 Proxy 下的 ws.close() 能正常触发 onclose。因此生产环境慎用 Proxy,仅限调试场景临时尝试。
- 若坚持用 Proxy,必须显式透传所有已知属性:
if (prop in target) return target[prop],不能只拦截send -
target上的url属性不可写,Proxy 的settrap 对其无效,别白费力气 - Proxy 返回的对象不是
instanceof WebSocket,某些依赖类型判断的库(如is-websocket)会失效
拦截器链封装比单点 hook 更适合复杂业务
当项目需要同时处理鉴权、日志、加解密、错误映射时,硬编码在重写逻辑里会迅速失控。此时应退一步,封装一个轻量 WSClient 类,把 window.WebSocket 重写降级为“自动注入客户端”的触发器,而非业务逻辑容器。
核心差异在于:钩子函数不直接操作原始 ws,而是接收上下文对象 { data, url, type: 'request' | 'response', timestamp },返回修改后的数据或 false 中断流程。这样业务代码和拦截逻辑完全解耦,也方便单元测试。
- 请求拦截器必须同步执行,异步(如 await token 刷新)会导致
send调用提前发生 - 响应拦截器收到的是原始
MessageEvent,若需 JSON 解析,应在拦截器内做try/catch,避免炸掉整个消息流 - 拦截器链顺序很重要:日志拦截器放最后,鉴权放最前;否则可能记录到未加密的明文,或在鉴权前就发出了请求
window,子 iframe 或 sandboxed 页面需单独注入;Service Worker 也无法拦截 WebSocket,它根本不在其作用域内。










