IPC (Inter-Process Communication) is the core mechanism of Electron architecture, used to pass messages and data between the main process and renderer processes.
IPC Basic Concepts
Electron's IPC module consists of two main parts:
- ipcMain: Used in the main process to receive messages from renderer processes
- ipcRenderer: Used in renderer processes to send messages to the main process
Basic Communication Methods
1. Renderer → Main (One-way Communication)
Send Message (No Response)
javascript// Renderer process const { ipcRenderer } = require('electron') ipcRenderer.send('channel-name', { data: 'Hello from renderer' }) // Main process const { ipcMain } = require('electron') ipcMain.on('channel-name', (event, data) => { console.log('Received:', data) })
Send Message and Wait for Response
javascript// Renderer process ipcRenderer.invoke('channel-name', { data: 'Hello' }) .then(response => { console.log('Response:', response) }) // Main process ipcMain.handle('channel-name', async (event, data) => { // Processing logic return { result: 'Success' } })
2. Main → Renderer (One-way Communication)
javascript// Main process mainWindow.webContents.send('channel-name', { data: 'Hello from main' }) // Renderer process ipcRenderer.on('channel-name', (event, data) => { console.log('Received:', data) })
3. Two-way Communication
javascript// Renderer process ipcRenderer.send('request-data', { id: 123 }) ipcRenderer.on('response-data', (event, data) => { console.log('Response:', data) }) // Main process ipcMain.on('request-data', (event, data) => { // Process request const result = processData(data) event.reply('response-data', result) })
Advanced Communication Patterns
1. Using invoke/handle for Asynchronous Communication
This method returns a Promise and is more suitable for asynchronous operations:
javascript// Renderer process async function fetchData() { try { const result = await ipcRenderer.invoke('fetch-data', { id: 1 }) return result } catch (error) { console.error('Error:', error) } } // Main process ipcMain.handle('fetch-data', async (event, { id }) => { // Can perform asynchronous operations const data = await database.query(id) return data })
2. Using contextBridge to Safely Expose APIs
javascript// preload.js const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld('electronAPI', { // Send message send: (channel, data) => ipcRenderer.send(channel, data), // Send and wait for response invoke: (channel, data) => ipcRenderer.invoke(channel, data), // Listen for messages on: (channel, callback) => { const subscription = (event, ...args) => callback(...args) ipcRenderer.on(channel, subscription) return () => ipcRenderer.removeListener(channel, subscription) }, // Remove listener removeListener: (channel, callback) => { ipcRenderer.removeListener(channel, callback) } }) // Renderer process window.electronAPI.send('channel-name', data) const response = await window.electronAPI.invoke('async-channel', data)
3. Using Event for Window-to-Window Communication
javascript// Main process - Send from window A to window B const windowA = BrowserWindow.fromWebContents(event.sender) const windowB = BrowserWindow.getAllWindows()[1] windowA.webContents.send('message-to-window-b', { data: 'Hello' }) // Renderer process of window B ipcRenderer.on('message-to-window-b', (event, data) => { console.log('Received from window A:', data) })
Communication Data Types
IPC supports passing the following data types:
- Basic types (String, Number, Boolean, null, undefined)
- Objects and arrays
- Buffer
- Error objects
- Date objects
Note: Cannot pass functions, DOM nodes, or objects with circular references
Best Practices
1. Use Clear Channel Names
javascript// Good practice ipcRenderer.send('user:login', credentials) ipcRenderer.send('file:read', filePath) // Bad practice ipcRenderer.send('message1', data) ipcRenderer.send('message2', data)
2. Error Handling
javascript// Renderer process try { const result = await ipcRenderer.invoke('async-operation', data) } catch (error) { console.error('Operation failed:', error) } // Main process ipcMain.handle('async-operation', async (event, data) => { try { const result = await process(data) return result } catch (error) { throw new Error(`Processing failed: ${error.message}`) } })
3. Clean Up Listeners
javascript// Renderer process const cleanup = window.electronAPI.on('channel-name', (data) => { console.log(data) }) // Clean up when component unmounts componentWillUnmount() { cleanup() }
4. Use TypeScript Type Definitions
typescript// types.ts interface IpcChannels { 'user:login': (credentials: Credentials) => Promise<User> 'file:read': (path: string) => Promise<string> } // preload.ts contextBridge.exposeInMainWorld('electronAPI', { invoke: <K extends keyof IpcChannels>( channel: K, ...args: Parameters<IpcChannels[K]> ): ReturnType<IpcChannels[K]> => ipcRenderer.invoke(channel, ...args) })
Performance Optimization
1. Reduce Communication Frequency
javascript// Bad practice - Frequent communication for (let i = 0; i < 1000; i++) { ipcRenderer.send('process-item', items[i]) } // Good practice - Batch processing ipcRenderer.send('process-items', items)
2. Use Worker Threads for Intensive Tasks
javascript// Main process ipcMain.handle('heavy-computation', async (event, data) => { const worker = new Worker('./worker.js') return new Promise((resolve, reject) => { worker.on('message', resolve) worker.on('error', reject) worker.postMessage(data) }) })
Security Considerations
- Validate Input Data: Validate all data from renderer processes in the main process
- Limit Channel Access: Only expose necessary IPC channels
- Use contextIsolation: Ensure renderer processes cannot directly access Node.js APIs
- Avoid Sensitive Data: Do not pass sensitive information in IPC messages
Common Questions
Q: What's the difference between invoke and send?A: invoke returns a Promise and is suitable for scenarios requiring responses; send is one-way communication and doesn't return results.
Q: How to communicate between multiple windows?A: Use the main process as an intermediary and send messages to specific windows using webContents.send().
Q: Is there a size limit for IPC communication?A: There's no hard limit theoretically, but it's recommended to use the file system or shared memory when passing large data.