electron+vue3全家桶+vite项目实战【二】基于开源脚手架快速实现多窗口管理

张开发
2026/4/16 9:17:23 15 分钟阅读

分享文章

electron+vue3全家桶+vite项目实战【二】基于开源脚手架快速实现多窗口管理
1. 为什么需要多窗口管理在开发Electron桌面应用时单窗口应用往往无法满足复杂业务需求。比如你可能需要主窗口作为控制中心子窗口展示详细数据独立设置窗口与主界面分离实现类似IDE的多文档编辑界面弹出临时调试窗口而不影响主流程传统手动创建窗口的方式存在几个痛点窗口生命周期难以维护窗口间通信代码冗余样式和状态管理混乱资源占用不可控我在实际项目中就遇到过这样的场景需要同时打开5个监控面板窗口每个窗口展示不同的实时数据。最初用原生API实现后发现内存泄漏严重窗口关闭后资源没有正确释放。后来改用脚手架提供的窗口管理方案内存占用直接下降了40%。2. 脚手架环境准备2.1 初始化项目首先确保你已经完成基础工程搭建如果还没完成可以参考我上一篇文章。这里我们继续使用electron-vite-vue这个明星脚手架npm create electron-vitelatest my-multiwin-app选择vue模板后你会得到一个标准项目结构├── src │ ├── main (主进程代码) │ ├── renderer (渲染进程代码) │ └── preload (预加载脚本) ├── electron.vite.config.js └── package.json2.2 关键依赖说明脚手架已经内置了多窗口管理所需的核心依赖electron-window-manager: 窗口生命周期管理vite-plugin-electron: 主进程HMR支持vue-router: 多窗口路由方案pinia: 跨窗口状态共享建议检查package.json确保这些依赖版本dependencies: { electron-window-manager: ^2.0.3, vite-plugin-electron: ^0.11.2, vue-router: ^4.1.6 }3. 多窗口创建实战3.1 基础窗口创建在src/main/windows目录下新建mainWindow.jsimport { BrowserWindow } from electron import path from path export function createMainWindow() { const win new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: path.join(__dirname, ../preload/index.js) } }) if (process.env.VITE_DEV_SERVER_URL) { win.loadURL(process.env.VITE_DEV_SERVER_URL) } else { win.loadFile(path.join(process.env.DIST, index.html)) } return win }然后在主进程(main/index.js)中调用import { createMainWindow } from ./windows/mainWindow app.whenReady().then(() { const mainWin createMainWindow() // 窗口管理器的注册 WindowManager.register(main, mainWin) })3.2 动态创建子窗口新建src/main/windows/childWindow.jsexport function createChildWindow(parent, config {}) { const win new BrowserWindow({ width: 800, height: 600, parent, // 关键参数设置父窗口 modal: true, // 设置为模态窗口 ...config }) // 加载子窗口专属页面 win.loadURL(${process.env.VITE_DEV_SERVER_URL}/child.html) return win }在渲染进程中通过IPC触发创建script setup import { ipcRenderer } from electron const openChild () { ipcRenderer.send(create-child-window, { title: 数据分析面板 }) } /script4. 窗口通信方案4.1 主进程与渲染进程通信在preload中暴露安全APIcontextBridge.exposeInMainWorld(electronAPI, { send: (channel, data) ipcRenderer.send(channel, data), on: (channel, func) ipcRenderer.on(channel, (event, ...args) func(...args)) })主进程监听事件ipcMain.on(create-child-window, (event, config) { const parent WindowManager.get(main) const child createChildWindow(parent, config) WindowManager.register(child_${Date.now()}, child) })4.2 窗口间直接通信利用MessagePort实现高性能通信// 主窗口 const { port1, port2 } new MessageChannel() childWindow.postMessage(port, null, [port1]) // 子窗口 window.addEventListener(message, (event) { if (event.data port) { const [port] event.ports port.onmessage (e) { console.log(收到消息:, e.data) } } })5. 生产环境优化5.1 内存管理技巧在窗口关闭时手动清理win.on(closed, () { // 释放引用 win null // 清理注册表 WindowManager.unregister(winId) // 强制GC仅在必要时 if (process.env.NODE_ENV production) { global.gc() } })5.2 窗口恢复策略在app启动时恢复上次的窗口布局const lastSession store.get(windowStates) app.on(ready, () { if (lastSession) { lastSession.forEach(state { createWindow(state) }) } else { createMainWindow() } }) app.on(before-quit, () { const states WindowManager.getAllStates() store.set(windowStates, states) })6. 常见问题排查6.1 窗口闪烁问题在创建窗口时添加这些配置new BrowserWindow({ backgroundColor: #2e2c29, show: false, // 先隐藏 webPreferences: { offscreen: false, backgroundThrottling: false } }) // 等页面加载完毕再显示 win.on(ready-to-show, () { win.show() })6.2 开发者工具异常建议在开发环境下这样加载devtoolsimport installExtension, { VUEJS_DEVTOOLS } from electron-devtools-installer app.whenReady().then(async () { try { await installExtension(VUEJS_DEVTOOLS) } catch (e) { console.error(Vue Devtools安装失败:, e) } })7. 进阶技巧7.1 窗口分组管理对于需要批量操作的窗口可以实现分组机制class WindowGroup { constructor(name) { this.name name this.windows new Map() } add(id, win) { this.windows.set(id, win) WindowManager.register(id, win) } broadcast(channel, ...args) { this.windows.forEach(win { if (!win.isDestroyed()) { win.webContents.send(channel, ...args) } }) } } // 使用示例 const analyticsGroup new WindowGroup(analytics) analyticsGroup.add(chart1, createChartWindow()) analyticsGroup.add(chart2, createChartWindow())7.2 窗口状态持久化使用electron-store保存窗口状态import Store from electron-store const schema { windowStates: { type: array, default: [] } } const store new Store({ schema }) // 保存状态 function saveWindowState(win) { const state { bounds: win.getBounds(), isMaximized: win.isMaximized(), url: win.webContents.getURL() } store.set(windows.${win.id}, state) }8. 项目结构建议经过多个项目实践我总结出这样的目录结构最合理src/ ├── main/ │ ├── windows/ │ │ ├── mainWindow.js │ │ ├── childWindow.js │ │ └── types/ (窗口类型定义) │ ├── managers/ │ │ ├── windowManager.js │ │ └── communication.js │ └── store/ (主进程状态) ├── renderer/ │ ├── windows/ │ │ ├── main/ │ │ └── child/ │ └── stores/ (Pinia状态) └── shared/ (公用代码)这种结构的特点是按功能而非技术角色划分窗口相关代码集中管理主进程和渲染进程有明确边界共享代码单独存放

更多文章