当前位置: 首页 > news >正文

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

1. 为什么需要多窗口管理?

在开发Electron桌面应用时,单窗口应用往往无法满足复杂业务需求。比如你可能需要:

  • 主窗口作为控制中心,子窗口展示详细数据
  • 独立设置窗口与主界面分离
  • 实现类似IDE的多文档编辑界面
  • 弹出临时调试窗口而不影响主流程

传统手动创建窗口的方式存在几个痛点:

  1. 窗口生命周期难以维护
  2. 窗口间通信代码冗余
  3. 样式和状态管理混乱
  4. 资源占用不可控

我在实际项目中就遇到过这样的场景:需要同时打开5个监控面板窗口,每个窗口展示不同的实时数据。最初用原生API实现后,发现内存泄漏严重,窗口关闭后资源没有正确释放。后来改用脚手架提供的窗口管理方案,内存占用直接下降了40%。

2. 脚手架环境准备

2.1 初始化项目

首先确保你已经完成基础工程搭建(如果还没完成可以参考我上一篇文章)。这里我们继续使用electron-vite-vue这个明星脚手架:

npm create electron-vite@latest my-multiwin-app

选择vue模板后,你会得到一个标准项目结构:

├── src │ ├── main (主进程代码) │ ├── renderer (渲染进程代码) │ └── preload (预加载脚本) ├── electron.vite.config.js └── package.json

2.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.js:

import { 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.js:

export 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: '数据分析面板' }) } </script>

4. 窗口通信方案

4.1 主进程与渲染进程通信

在preload中暴露安全API:

contextBridge.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 开发者工具异常

建议在开发环境下这样加载devtools:

import 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/ (公用代码)

这种结构的特点是:

  1. 按功能而非技术角色划分
  2. 窗口相关代码集中管理
  3. 主进程和渲染进程有明确边界
  4. 共享代码单独存放
http://www.jsqmd.com/news/649369/

相关文章:

  • Cartographer实战:如何用二维码和反光板提升SLAM定位精度(附避坑指南)
  • HDMI 1.4 协议链路层详解:从TMDS编码到视频传输时序
  • StructBERT在网络安全中的应用:恶意邮件与钓鱼文本相似度识别
  • 别再只会点‘Run All’了!Vivado Simulator波形窗口的5个隐藏技巧,让调试效率翻倍
  • 英语并非人生必修课,中文才是文明与未来的主流
  • 别再只玩ChatGPT了!试试用GPT-4V和Gemini玩转多模态AI:从图片分析到视频理解实战
  • 深入解析WebRTC协议在FFmpeg中的推流与拉流实现
  • 移远EC600S-CN实战:HTTP(S) AT指令详解与OneNET设备状态监控应用
  • AI建站避坑指南:10个高频问题与风险防范方案
  • 如何为Stencil开发自定义扩展插件:完整指南
  • C语言实战:基于LU分解法的高效矩阵求逆与行列式计算
  • WarcraftHelper:让经典魔兽争霸III完美适配现代系统的终极方案
  • 技术模板方法中的步骤定义与扩展点
  • WeChatExporter完整指南:如何在Mac上快速备份微信聊天记录
  • 5步终极配置:让PS4/PS5手柄在PC上发挥完整游戏潜力的专业指南
  • KeymouseGo终极指南:5分钟掌握鼠标键盘自动化神器
  • ACE-Step效果展示:看看AI生成的音乐有多惊艳
  • 推荐2款Windows实用小工具,1款适合老师使用
  • 终极指南:Semantic-UI-React状态管理高级模式——Context与全局状态完全掌握
  • 3步掌握MCA Selector:终极Minecraft区块管理神器
  • 被对方拉黑了,还有必要去联系吗?
  • 三步搞定《经济研究》专业论文排版:LaTeX模板终极指南
  • 3大突破:RePKG如何彻底改变Wallpaper Engine资源访问模式
  • 别再手动写查询表单了!用Ant Design ProTable的columns自动生成,效率翻倍(附实战避坑点)
  • 保姆级教程:在STM32F4上分别跑通ThreadX和FreeRTOS的‘Hello World’(附性能实测对比)
  • win11下安装labelme
  • TypeScript实战:零依赖实现4种自定义UUID生成方案
  • 12. C++17新特性-std::optional
  • 纯前端实现视频封面生成:Canvas与Video API的实战应用
  • 3分钟解锁Unity游戏无限可能:MelonLoader终极安装秘籍