sessionstorage 适合存 websocket 轻量元数据(如状态、时间戳、重连次数、client_id),需用唯一键名隔离多标签,配合 visibilitychange/beforeunload 主动清理,并在 onopen 中强制刷新状态以确保干净重连。

用 sessionStorage 存 WebSocket 连接状态,核心不是“存得多”,而是“存得准、清得快、不跨会话污染”。它天然的页面级生命周期(关闭标签页即清空)恰好匹配 WebSocket 单实例连接场景,能有效规避多标签/多窗口下重连逻辑互相干扰、状态错乱的问题。
只存关键上下文,不存连接实例本身
sessionStorage 只适合存轻量、可序列化的连接元数据,比如:
-
当前连接状态标识:如
"connecting"、"open"、"reconnecting"(避免仅靠ws.readyState判断,因断连瞬间该值可能仍为1) -
最后一次成功通信时间戳:
"last_active_ts": 1716211452387,用于心跳超时判定 -
重连尝试次数:
"reconnect_count": 3,配合指数退避算法使用 - 服务端分配的 session_id 或 client_id(非敏感),用于重连后同步校验
切忌存 WebSocket 实例、回调函数或闭包引用——这些无法序列化,且会导致内存泄漏。
用键名命名空间隔离,防多标签冲突
同一域名下所有标签页共享同一个 sessionStorage 对象。若不加区分,A 标签页断连触发重连,B 标签页可能误读其状态并执行错误操作。解决方案是为每个 WebSocket 实例绑定唯一键前缀:
- 生成方式示例:
const storageKey = `ws_${performance.now().toString(36)}`(短毫秒级唯一) - 或基于业务上下文:
`ws_chat_${roomId}`、`ws_trade_${userId}` - 连接建立后立即写入:
sessionStorage.setItem(`${storageKey}:state`, 'open') - 销毁连接前清除:
sessionStorage.removeItem(storageKey + ':state')
与 onbeforeunload / visibilitychange 联动,主动降级
用户切后台或关闭标签页时,WebSocket 实际已不可用,但 onclose 可能延迟触发甚至不触发(尤其在 Android WebView 中)。此时应主动标记“预期断连”:
- 监听
visibilitychange:document.addEventListener('visibilitychange', () => { if (document.hidden) sessionStorage.setItem(`${key}:expected_close`, 'true') }) - 监听
beforeunload做最后清理:window.addEventListener('beforeunload', () => sessionStorage.removeItem(`${key}:state`)) - 重连逻辑启动前先检查:
if (sessionStorage.getItem(`${key}:expected_close`) === 'true'),则跳过本次重连,等待下次显式初始化
重连成功后立即刷新状态,拒绝陈旧缓存
重连不是简单恢复连接,而是重建上下文。必须在 onopen 回调中强制覆盖 sessionStorage 状态:
- 清除旧重试计数:
sessionStorage.removeItem(`${key}:reconnect_count`) - 更新活跃时间:
sessionStorage.setItem(`${key}:last_active_ts`, Date.now().toString()) - 写入新状态:
sessionStorage.setItem(`${key}:state`, 'open') - 若需服务端同步,此时再发
{"cmd":"sync","last_seq_id":...},而非依赖缓存值
这样可确保每次重连都是“干净启动”,不会把上一次失败的中间态带入新连接。











