BroadcastChannel 可实现同源页面间实时 UI 同步,但需满足协议+域名+端口完全一致、浏览器支持(Safari 15.4+起)、非关键业务;须防重复/乱序/丢失,加时间戳或 ID 校验,及时 close,全局单例管理。
能用 BroadcastChannel 实现实时 UI 同步,但必须满足同源(协议 + 域名 + 端口完全一致),且不能依赖它做关键业务状态的 100% 可靠传递。
同源不是“域名一样就行”——https://example.com 和 https://admin.example.com 不同源,http://localhost:3000 和 http://localhost:8080 也不行。端口不同就直接隔离。
兼容性方面:Chrome、Firefox、Edge(Chromium 内核)、Safari 15.4+ 都支持;旧版 Safari 或 IE 完全不支持,别试。
BroadcastChannel,用户无感知,消息发不出也收不到iframe 要参与通信,必须和父页同源,且自己也要执行 new BroadcastChannel('xxx')
storage 事件,或用 localStorage 触发后补发一次postMessage() 只支持结构化克隆(structured clone),不能传 function、undefined、Symbol、Promise 或带循环引用的对象。传了会静默失败,控制台可能只报 “DataCloneError”。
{ type: 'UI_UPDATE', payload: { theme: 'dark', unread: 5 } }
postMessage() 前做复杂计算或深拷贝,这会阻塞主线程,影响 UI 响应BroadcastChannel 不保证顺序,也不保证送达——页面正在关闭、内存紧张、或刚打开还没注册监听时,消息就丢了。UI 同步类场景对“偶尔丢失”相对宽容,但得防住重复执行。
location.reload() 或 router.push() 这类副作用强的操作,先校验是否已处于目标状态timestamp 或 id 字段,配合本地状态比对,避免老消息覆盖新状态onmessage 回调里做耗时操作(如 JSON.parse 大字符串、DOM 批量重绘),否则会卡住后续消息处理channel.close(),否则容易内存泄漏(尤其 SPA 中反复进/出同一页面)每个 new BroadcastChannel('xxx') 都是独立实例。如果组件多次挂载、或工具函数被反复 import,很容易无意中创建多个同名频道,导致重复监听、重复响应。
channel 实例挂到 window 或用模块单例导出,比如 export const channel = new BroadcastChannel('ui-sync')
setup() 里直接 new,改用 onBeforeUnmount(() => channel.close()) 配合单例useEffect(() => { return () => channel.close() }, []),但前提是 channel 是模块顶层定义的new BroadcastChannel('x') 不会报错,但会导致同一消息被自己收到多次真正难的不是写通广播,而是判断哪些 UI 状态值得广播、哪些该由本地逻辑决定。比如“当前播放进度”适合广播,“输入框临时草稿”就不该——后者本就不该跨标签同步。