乐闻世界logo
搜索文章和话题

Electron Multi-Window Management and Communication

2月18日 10:40

Electron applications often need to create multiple windows to implement complex features, such as main windows, settings windows, popups, etc. This article will detail multi-window management and inter-window communication in Electron.

Creating Multiple Windows

1. Basic Window Creation

javascript
// main.js const { app, BrowserWindow } = require('electron') let mainWindow let settingsWindow app.whenReady().then(() => { createMainWindow() }) function createMainWindow() { mainWindow = new BrowserWindow({ width: 1200, height: 800, webPreferences: { nodeIntegration: false, contextIsolation: true, preload: path.join(__dirname, 'preload.js') } }) mainWindow.loadFile('index.html') mainWindow.on('closed', () => { mainWindow = null }) } function createSettingsWindow() { if (settingsWindow) { settingsWindow.focus() return } settingsWindow = new BrowserWindow({ width: 600, height: 400, parent: mainWindow, // Set as child window of main window modal: true, // Modal window webPreferences: { nodeIntegration: false, contextIsolation: true, preload: path.join(__dirname, 'preload.js') } }) settingsWindow.loadFile('settings.html') settingsWindow.on('closed', () => { settingsWindow = null }) }

2. Window Types

javascript
// Main window const mainWindow = new BrowserWindow({ width: 1200, height: 800, title: 'My App', icon: path.join(__dirname, 'icon.png') }) // Settings window const settingsWindow = new BrowserWindow({ width: 600, height: 400, title: 'Settings', parent: mainWindow, modal: true }) // Popup window const popupWindow = new BrowserWindow({ width: 400, height: 300, parent: mainWindow, show: false, // Don't show initially autoHideMenuBar: true }) // Tool window const toolWindow = new BrowserWindow({ width: 300, height: 200, frame: false, // Frameless alwaysOnTop: true, // Always on top transparent: true // Transparent background })

Window Management

1. Window Reference Management

javascript
// main.js const windows = new Map() function createWindow(id, options) { const win = new BrowserWindow(options) windows.set(id, win) win.on('closed', () => { windows.delete(id) }) return win } function getWindow(id) { return windows.get(id) } function closeWindow(id) { const win = getWindow(id) if (win) { win.close() } } function closeAllWindows() { windows.forEach((win, id) => { win.close() }) windows.clear() }

2. Window State Management

javascript
// main.js const windowState = new Map() function saveWindowState(id, win) { const [width, height] = win.getSize() const [x, y] = win.getPosition() const isMaximized = win.isMaximized() const isFullScreen = win.isFullScreen() windowState.set(id, { width, height, x, y, isMaximized, isFullScreen }) } function restoreWindowState(id, win) { const state = windowState.get(id) if (state) { win.setSize(state.width, state.height) win.setPosition(state.x, state.y) if (state.isMaximized) { win.maximize() } if (state.isFullScreen) { win.setFullScreen(true) } } } // Usage app.whenReady().then(() => { const win = createWindow('main', { width: 1200, height: 800 }) restoreWindowState('main', win) win.on('close', () => { saveWindowState('main', win) }) })

3. Window Lifecycle Management

javascript
// main.js app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createMainWindow() } }) // Prevent accidental closing app.on('before-quit', (event) => { const windows = BrowserWindow.getAllWindows() const hasUnsavedChanges = windows.some(win => win.webContents.executeJavaScript('hasUnsavedChanges()') ) if (hasUnsavedChanges) { event.preventDefault() // Prompt user to save } })

Inter-Window Communication

1. Main Process as Intermediary

javascript
// main.js const { ipcMain } = require('electron') // Send from window A to window B ipcMain.on('send-to-window-b', (event, data) => { const windowB = getWindow('window-b') if (windowB) { windowB.webContents.send('message-from-a', data) } }) // Broadcast to all windows ipcMain.on('broadcast', (event, data) => { const windows = BrowserWindow.getAllWindows() windows.forEach(win => { if (win !== BrowserWindow.fromWebContents(event.sender)) { win.webContents.send('broadcast-message', data) } }) })

2. Direct Window Communication

javascript
// main.js // Get specific window function sendToWindow(windowId, channel, data) { const win = getWindow(windowId) if (win) { win.webContents.send(channel, data) } } // Call from renderer process ipcMain.handle('send-to-window', (event, { windowId, channel, data }) => { sendToWindow(windowId, channel, data) })

3. Using Event Bus

javascript
// main.js const EventEmitter = require('events') const eventBus = new EventEmitter() // Subscribe to events eventBus.on('window-event', (data) => { console.log('Received event:', data) }) // Publish events eventBus.emit('window-event', { message: 'Hello' }) // Use between windows ipcMain.on('window-a-event', (event, data) => { eventBus.emit('window-a-event', data) }) ipcMain.on('window-b-event', (event, data) => { eventBus.emit('window-b-event', data) })

Window Data Sharing

1. Using Main Process Storage

javascript
// main.js const sharedData = new Map() ipcMain.handle('set-shared-data', (event, { key, value }) => { sharedData.set(key, value) return true }) ipcMain.handle('get-shared-data', (event, key) => { return sharedData.get(key) }) ipcMain.handle('delete-shared-data', (event, key) => { return sharedData.delete(key) })

2. Using localStorage

javascript
// renderer.js // Set data localStorage.setItem('shared-key', JSON.stringify(data)) // Get data const data = JSON.parse(localStorage.getItem('shared-key')) // Listen for changes window.addEventListener('storage', (event) => { if (event.key === 'shared-key') { const newData = JSON.parse(event.newValue) // Handle data changes } })

3. Using IndexedDB

javascript
// renderer.js const request = indexedDB.open('SharedDB', 1) request.onupgradeneeded = (event) => { const db = event.target.result const objectStore = db.createObjectStore('sharedData', { keyPath: 'key' }) } request.onsuccess = (event) => { const db = event.target.result // Add data const transaction = db.transaction(['sharedData'], 'readwrite') const objectStore = transaction.objectStore('sharedData') objectStore.add({ key: 'myKey', value: 'myValue' }) // Get data const getRequest = objectStore.get('myKey') getRequest.onsuccess = (event) => { console.log(event.target.result) } }

Window Synchronization

1. State Synchronization

javascript
// main.js const windowStates = new Map() ipcMain.on('window-state-change', (event, state) => { const windowId = getWindowId(event.sender) windowStates.set(windowId, state) // Notify other windows const windows = BrowserWindow.getAllWindows() windows.forEach(win => { if (win !== BrowserWindow.fromWebContents(event.sender)) { win.webContents.send('window-state-update', { windowId, state }) } }) })

2. Data Synchronization

javascript
// main.js ipcMain.on('data-update', (event, data) => { // Save data saveData(data) // Notify all windows const windows = BrowserWindow.getAllWindows() windows.forEach(win => { win.webContents.send('data-updated', data) }) })

3. Action Synchronization

javascript
// main.js ipcMain.on('perform-action', (event, action) => { // Execute action const result = performAction(action) // Notify all windows const windows = BrowserWindow.getAllWindows() windows.forEach(win => { win.webContents.send('action-performed', { action, result }) }) })

Window Layout Management

1. Window Position Management

javascript
// main.js function arrangeWindows() { const windows = BrowserWindow.getAllWindows() const screenWidth = require('electron').screen.getPrimaryDisplay().workAreaSize.width const screenHeight = require('electron').screen.getPrimaryDisplay().workAreaSize.height windows.forEach((win, index) => { const [width, height] = win.getSize() const x = (index % 2) * (screenWidth / 2) const y = Math.floor(index / 2) * (screenHeight / 2) win.setPosition(x, y) }) }

2. Window Size Management

javascript
// main.js function resizeWindows(width, height) { const windows = BrowserWindow.getAllWindows() windows.forEach(win => { win.setSize(width, height) }) } function maximizeAllWindows() { const windows = BrowserWindow.getAllWindows() windows.forEach(win => { win.maximize() }) } function minimizeAllWindows() { const windows = BrowserWindow.getAllWindows() windows.forEach(win => { win.minimize() }) }

3. Window Focus Management

javascript
// main.js function focusWindow(windowId) { const win = getWindow(windowId) if (win) { win.focus() } } function focusNextWindow() { const windows = BrowserWindow.getAllWindows() const focusedWindow = BrowserWindow.getFocusedWindow() if (focusedWindow) { const currentIndex = windows.indexOf(focusedWindow) const nextIndex = (currentIndex + 1) % windows.length windows[nextIndex].focus() } }

Best Practices

1. Window Creation Optimization

javascript
// main.js // Lazy window creation function createWindowLazy(id, options) { if (getWindow(id)) { return getWindow(id) } const win = new BrowserWindow({ show: false, // Don't show initially ...options }) win.once('ready-to-show', () => { win.show() }) windows.set(id, win) return win }

2. Memory Management

javascript
// main.js // Close inactive windows function cleanupInactiveWindows() { const windows = BrowserWindow.getAllWindows() const now = Date.now() windows.forEach(win => { const lastActive = win.getLastFocusedWebContents()?.getLastActiveTime() || 0 const inactiveTime = now - lastActive if (inactiveTime > 30 * 60 * 1000) { // 30 minutes win.close() } }) } // Periodic cleanup setInterval(cleanupInactiveWindows, 5 * 60 * 1000) // 5 minutes

3. Error Handling

javascript
// main.js function safeCreateWindow(id, options) { try { const win = new BrowserWindow(options) windows.set(id, win) win.on('unresponsive', () => { console.error(`Window ${id} became unresponsive`) }) win.on('responsive', () => { console.log(`Window ${id} is responsive again`) }) win.on('crashed', (event, killed) => { console.error(`Window ${id} crashed. Killed: ${killed}`) // Try to recreate window recreateWindow(id, options) }) return win } catch (error) { console.error(`Failed to create window ${id}:`, error) return null } }

Common Questions

Q: How to prevent window from closing?A: Listen to beforeunload event:

javascript
// renderer.js window.addEventListener('beforeunload', (event) => { if (hasUnsavedChanges()) { event.preventDefault() event.returnValue = false } })

Q: How to transfer large amounts of data between windows?A: Use main process as intermediary, or use shared storage like IndexedDB.

Q: How to implement window dragging?A: Use -webkit-app-region CSS property:

css
.title-bar { -webkit-app-region: drag; }

Q: How to implement window transparency?A: Set transparent: true and use CSS:

javascript
const win = new BrowserWindow({ transparent: true, frame: false })

标签:Electron