跳到主要内容

多窗口架构怎么设计?窗口间通信方案有哪些?

考察点​

  • 窗口生命周期管理(创建/复用/销毁)
  • 窗口间通信方案的选型与取舍

参考答案​

1. 窗口管理器设计​

多窗口应用需要一个统一的 WindowManager,核心是 按业务 key 管理窗口实例:

class WindowManager {
constructor() {
this.windows = new Map(); // 业务 key -> BrowserWindow
}

// 打开或聚焦窗口(避免重复创建)
open(key, options) {
const existing = this.windows.get(key);
if (existing && !existing.isDestroyed()) {
existing.show();
existing.focus();
return existing;
}
const win = new BrowserWindow(options);
this.windows.set(key, win);
// 关闭时清理引用,防止内存泄漏
win.on('closed', () => this.windows.delete(key));
return win;
}

broadcast(channel, data) {
// 给所有窗口广播消息
for (const win of this.windows.values()) {
if (!win.isDestroyed()) win.webContents.send(channel, data);
}
}
}

要点:

  • 用 Map 按业务 key 复用窗口,防止用户开 10 个相同设置页
  • closed 事件里必须 delete 引用,否则内存泄漏
  • 提供 broadcast / sendTo(key) 统一消息出口

2. 窗口间通信方案对比​

方案原理适用场景缺点
主进程中转A 窗口 send → 主进程路由 → B 窗口 send通用、可靠主进程成为瓶颈,延迟多一跳
MessagePort 直连主进程建 MessageChannelMain,把 port 分别转交给两个窗口高频/大数据通信建立通道的握手复杂
BroadcastChannelWeb 原生 API,同 origin 页面广播简单状态同步受同源限制,无法指定接收方
localStorage + storage 事件同 partition 下共享存储低频小数据同步性能差,大数据不适用

3. 推荐实践​

  • 控制类消息(打开窗口、状态变更通知):走主进程中转,简单可靠
  • 高频/大数据通道(如日志实时传输、音视频帧):建立 MessagePort 直连,绕过主进程
  • 全局状态(主题、登录态):主进程做单一数据源,变更后 broadcast,各窗口订阅
// 主进程:为两个窗口建立直连通道
const { port1, port2 } = new MessageChannelMain();
winA.webContents.postMessage('port', null, [port1]);
winB.webContents.postMessage('port', null, [port2]);
// 之后 A、B 窗口通过各自的 port 直接通信,不经过主进程

面试话术​

我的原则是"主进程做路由不做搬运":控制信令走主进程中转保证可靠;高频数据用 MessageChannelMain 建直连通道,避免主进程成为性能瓶颈;全局状态由主进程持有、广播同步。

常见追问​

  • 窗口 A 给窗口 B 发消息,B 还没加载完怎么办? 主进程侧做消息队列,等 B 的 did-finish-load 后补发;或者 B ready 后主动 pull。
  • 几十个窗口 broadcast 有性能问题吗? 每个 send 都是一次结构化克隆 + IPC,窗口多时应按订阅关系定向发送,而不是无脑广播。