我做了一个小实验:在浏览器中打开同一聊天会话的两个标签页,只在标签页 A 发送消息,完全不操作标签页 B。结果是,B 会自动出现新消息,以及后续的 AI 回复。
第一反应很自然:这是 WebSocket 吗?
简短答案是:它很可能使用了某种实时更新机制,但仅凭这个现象,不能确认具体就是 WebSocket,更不能确认页面一定使用了 BroadcastChannel 或 localStorage。
从现象中能确定什么
两个标签页都能在不刷新的情况下看到同一会话的新增内容,至少说明了两件事:
- 服务端保存了会话状态。消息并不只存在于发送消息的那个页面内存中。
- 页面具备主动获得增量更新的能力。标签页 B 不需要重新加载页面,就能接收或拉取最新消息。
如果同一个会话还能在另一台电脑或手机 App 上同步,那么服务端的消息分发就是必需的。浏览器内的跨标签通信只能覆盖同一浏览器、同一源下的页面,无法承担跨设备同步。
它可能怎样实现
聊天产品通常由两层能力组成:服务端的会话同步,以及浏览器侧的状态协调。但具体产品采用哪些技术,需要证据确认。
服务端:把会话更新送到每个打开的页面
标签页 A 发送消息后,服务端会写入会话记录,并将新消息和模型生成的增量内容发送给订阅该会话的客户端。常见的传输方式包括:
- WebSocket:浏览器与服务端维持一条双向长连接,适合消息发送和流式回复。
- SSE:服务端通过单向事件流持续推送,客户端仍可用普通 HTTP 请求发送消息。
- HTTP 流式响应:发送消息的请求本身持续返回模型生成的文本片段。
- 轮询或长轮询:客户端定期或长期等待服务端返回新数据。实时感通常会略弱,但也能实现自动更新。
因此,看到流式输出或另一个标签页自动更新,只能说明存在“增量更新通道”,不能单独证明它是 WebSocket。
浏览器:同源标签页之间可选的本地协调
同一浏览器、同一站点的多个标签页,也可以额外使用浏览器提供的本地通信机制:
- **
BroadcastChannel**:一个标签页向同源的其他标签页发布事件,适合即时同步局部状态。 storage事件:一个页面写入localStorage后,其他同源页面会收到变化通知。- SharedWorker:多个标签页共享一个 Worker,由它统一管理连接或状态。
这些机制可以减少重复请求、协调未读状态或让 UI 更快反映本地变化。不过,它们不是双标签同步的必要条件:两个标签页各自从服务端接收推送,已经足够让人感觉“实时”。
一个更可靠的流程图
下面是与实验现象一致、但不预设具体协议的最小模型:
标签页 A 发送消息
|
v
服务端写入会话并生成增量更新
|
+-------------------+
| |
v v
标签页 A 的更新通道 标签页 B 的更新通道
| |
v v
两个页面分别更新 UI
如果站点额外使用了浏览器内广播,流程可能多出一条本地路径:
标签页 A 更新本地状态 -- BroadcastChannel / storage --> 标签页 B
但这条路径应当被看作优化假设,而不是从页面现象直接得出的结论。
如何验证,而不是猜测
想确认某个具体网站的实现,可以打开浏览器开发者工具进行只读观察:
- 在 Network 面板中筛选
WS,查看是否存在 WebSocket 连接及其 Frames。 - 查找类型为
event-stream的请求,判断是否使用 SSE。 - 发送一条消息,观察是否存在长时间保持打开的
fetch/XHR 请求,以及响应是否分块返回。 - 在 Sources 中搜索
BroadcastChannel、localStorage、SharedWorker和WebSocket,寻找前端代码是否调用这些 API。 - 分别在两个标签页打开 Network,观察标签页 B 是否独立收到服务端数据。若是,它本身的服务端连接就足以解释同步现象。
调试时应避免公开会话链接、Cookie、请求头或 WebSocket 帧中的账号和会话数据。
结论
“两个相同聊天标签页实时同步”最稳妥的解释是:服务端维护会话状态,并向打开该会话的客户端提供增量更新。
WebSocket 是常见且合理的候选方案,但并非唯一答案;BroadcastChannel、storage 事件和 SharedWorker 也可能参与同浏览器标签页的状态协调,却不能仅从同步效果推断它们一定存在。要把“可能使用”写成“确定使用”,仍需要开发者工具中的网络或代码证据。