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:
javascriptconst win = new BrowserWindow({ transparent: true, frame: false })