多窗口架构怎么设计?窗口间通信方案有哪些?
考察点
- 窗口生命周期管理(创建/复用/销毁)
- 窗口间通信方案的选型与取舍
参考答案
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 分别转交给两个窗口 | 高频/大数据通信 | 建立通道的握手复杂 |
| BroadcastChannel | Web 原生 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,窗口多时应按订阅关系定向发送,而不是无脑广播。