WeakMap 不跨标签页,仅用于单页内安全绑定异步状态与 DOM/实例,避免内存泄漏;跨页通信由 BroadcastChannel 等机制承担,Setter 协调本地更新与 WeakMap 查找响应者。

用 Setter 配合 WeakMap 实现异步标签页通信,核心不是让 WeakMap 跨标签页工作(它不能),而是用它在单页内安全绑定异步状态与 DOM 元素或 ViewModel 实例,避免内存泄漏,再通过标准跨页通信机制(如 BroadcastChannel、localStorage 事件、SharedWorker)触发同步行为。WeakMap 只负责“本页内谁该响应这次更新”,不负责传输数据本身。
WeakMap 不跨页,但能守住本页引用安全
WeakMap 的键必须是对象,且不阻止垃圾回收——这正好适合绑定 DOM 元素、组件实例或控制器对象。比如一个标签页中的 TabItem 组件被销毁时,WeakMap 自动丢弃对应条目,不会残留 handler 引用导致内存泄漏。
- 不要把 WeakMap 当作跨页存储:它只存在于当前 JavaScript 执行上下文,不同标签页有各自独立的 WeakMap 实例
- 适合存“响应逻辑”而非“数据”:例如存一个 onSyncReceived 回调函数,当收到 BroadcastChannel 消息时,用 WeakMap 查出哪个 TabItem 实例该刷新 UI
- Setter 在这里起协调作用:当外部消息到达,通过 setter 触发本地状态更新,并查 WeakMap 找到关联视图做响应
Setter + WeakMap 的典型协作模式
定义一个管理类,用 WeakMap 缓存组件实例与其异步处理逻辑的映射,用 setter 封装状态变更和通知触发:
- 在组件挂载时,用 this(组件实例)作为 WeakMap 的 key,存入一个包含 updateUI 方法的对象
- 提供一个 setter,例如 set syncData(value):内部先更新本地数据,再遍历 WeakMap 中所有注册项,调用其 updateUI
- 这个 setter 不直接发送跨页消息,而是由业务逻辑在合适时机(如用户点击保存)调用 BroadcastChannel.postMessage()
- 监听 message 事件后,同样走这个 setter,让所有已注册的活跃组件响应
配合 BroadcastChannel 完成真正跨页通信
WeakMap 管本页,BroadcastChannel 管跨页。两者职责分明:
- 初始化一个全局 BroadcastChannel('tab-sync'),所有同源标签页都可收发
- 发送方:调用 channel.postMessage({ type: 'data-updated', payload })
- 接收方:监听 message,解析后调用本地管理器的 setter,例如 syncManager.syncData = payload
- setter 内部通过 WeakMap 找出当前仍存活的 TabItem 实例,逐个更新,不强持引用,不报错
为什么不用 StrongReferenceMessenger 或事件总线?
在纯浏览器多标签页场景中,MVVM Toolkit 的 Messenger(如 WeakReferenceMessenger)运行于单个进程内,不跨渲染进程。它无法感知其他标签页的存在。而 BroadcastChannel 是浏览器原生支持的跨标签页通信机制,轻量、可靠、无依赖。
- WeakReferenceMessenger 适合同一页面内 ViewModel 与 View 的解耦,不适合跨页
- localStorage + storage 事件虽可用,但只在值变化时触发,且无法传递复杂对象(需 JSON 序列化),不如 BroadcastChannel 直接传结构化数据
- SharedWorker 更复杂,兼容性略低,对简单标签页同步属于过度设计

















