Introduction to Electron Basics
Installing Node.js and npm
Electron relies on Node.js and npm (Node Package Manager).
Installation Steps
Download Node.js:
- Visit the Node.js official website.
- Recommended LTS version (e.g., 16.x or 18.x; as of February 2025, use the latest LTS).
Installation:
- Windows/Mac: Run the installer.
- Linux:
sudo apt update
sudo apt install nodejs npmVerify Installation:
node -v # Example output: v18.18.0
npm -v # Example output: 9.8.1Notes
- Ensure npm is compatible with the Node.js version.
Installing Electron
Global Installation (Optional)
npm install -g electron
electron --version # Verify installationProject Local Installation (Recommended)
Create Project Directory:
mkdir MyFirstElectronApp
cd MyFirstElectronApp
npm init -yInstall Electron:
npm install electron --save-devModify package.json
{
"name": "my-first-electron-app",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^28.2.2"
}
}Creating Your First Electron Application
Create Main Process File (main.js)
const { app, BrowserWindow } = require('electron');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
win.loadFile('index.html');
}
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});Create Renderer Process File (index.html)
<!DOCTYPE html>
<html>
<head>
<title>Hello, Electron!</title>
</head>
<body>
<h1>Hello, Electron!</h1>
</body>
</html>Run the Application
npm startAnalysis
main.js: Defines the main process and creates the window.index.html: Defines the UI for the renderer process.npm start: Launches Electron.
Using Electron’s Main and Renderer Processes
Main Process Calls
// main.js
const { ipcMain } = require('electron');
ipcMain.on('message-from-renderer', (event, arg) => {
console.log('Renderer says:', arg);
event.reply('message-to-renderer', 'Hello from main!');
});Renderer Process Calls
<!DOCTYPE html>
<html>
<head>
<title>Hello, Electron!</title>
</head>
<body>
<h1>Hello, Electron!</h1>
<button onclick="sendMessage()">Send Message</button>
<script>
const { ipcRenderer } = require('electron');
function sendMessage() {
ipcRenderer.send('message-from-renderer', 'Hello from renderer!');
ipcRenderer.on('message-to-renderer', (event, arg) => {
console.log(arg); // "Hello from main!"
});
}
</script>
</body>
</html>Analysis
ipcMain: Main process receives messages.ipcRenderer: Renderer process sends and receives messages.
Electron Fundamentals
Understanding Electron Architecture: Main and Renderer Processes
- Main Process:
- Runs Node.js, controls application lifecycle.
- Manages windows and system interactions.
- Renderer Process:
- Runs Chromium, renders UI.
- Each window has its own renderer process.
Architecture Diagram
[Main Process]
├── Creates and manages windows
└── Interacts with system
[Renderer Process 1] [Renderer Process 2]
└── UI Rendering └── UI RenderingMain Process: Controlling Application Lifecycle
Extended Example (main.js)
const { app, BrowserWindow, Menu } = require('electron');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
win.loadFile('index.html');
// Custom Menu
const menu = Menu.buildFromTemplate([
{ label: 'File', submenu: [{ label: 'Quit', click: () => app.quit() }] },
]);
Menu.setApplicationMenu(menu);
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});Analysis
app.whenReady: Creates window when the app is ready.Menu: Controls application menu.
Renderer Process: Handling User Interface and Web Rendering
Extended Example (index.html)
<!DOCTYPE html>
<html>
<head>
<title>Hello, Electron!</title>
</head>
<body>
<h1>Hello, Electron!</h1>
<button onclick="sendMessage()">Send Message</button>
<script>
const { ipcRenderer } = require('electron');
function sendMessage() {
ipcRenderer.send('message-from-renderer', 'Hello from renderer!');
ipcRenderer.on('message-to-renderer', (event, arg) => {
console.log(arg);
});
}
</script>
</body>
</html>Analysis
- HTML/CSS: Defines UI and styles.
- JavaScript: Handles interaction logic.
HTML, CSS, JavaScript in Electron
- HTML: Structures the interface.
- CSS: Styles components.
- JavaScript:
- Main Process: Uses Node.js APIs.
- Renderer Process: Combines DOM and Electron APIs.
Example: Dynamic Styling
<!DOCTYPE html>
<html>
<head>
<style>
.dynamic { color: blue; }
</style>
</head>
<body>
<h1 class="dynamic">Hello, Electron!</h1>
</body>
</html>Application Structure
Project Directory Structure
MyFirstElectronApp/
├── main.js # Main process file
├── index.html # Renderer process entry
├── package.json # Project configuration
├── node_modules/ # Dependencies
├── assets/ # Static resources (e.g., images)Main Process File (main.js)
- Functionality: Window management, event listening.
- Extension:
const { app, BrowserWindow } = require('electron');
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
win.loadFile('index.html');
}
app.whenReady().then(createWindow);Renderer Process Files (HTML, CSS, JavaScript)
preload.js
const { contextBridge } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
sendMessage: (msg) => require('electron').ipcRenderer.send('message-from-renderer', msg),
});index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello, Electron!</title>
</head>
<body>
<h1>Hello, Electron!</h1>
<button onclick="window.electronAPI.sendMessage('Hello')">Send Message</button>
</body>
</html>Analysis
preload.js: Safely exposes APIs.- Renderer Process: Uses preload script to communicate with the main process.
Electron Core Architecture
Division of Responsibilities: Main and Renderer Processes
Main Process
Core Responsibilities
// Main Process Typical Responsibilities Example
const { app, BrowserWindow, ipcMain } = require('electron');
// 1. Application Lifecycle Management
app.whenReady().then(() => {
// Create Window
});
// 2. System-Level Functionality Control
const fs = require('fs');
fs.writeFileSync('/path/to/file', 'data'); // Direct File System Access
// 3. Window Management
const win = new BrowserWindow({ /* Configuration */ });
win.loadURL('https://example.com');
// 4. Native Menus and Dialogs
const { Menu, dialog } = require('electron');
Menu.setApplicationMenu(/* Custom Menu */);
dialog.showOpenDialog(/* File Selection Dialog */);Privileged Capabilities
- System-level API access (file system, network, native dialogs)
- Window lifecycle control
- Native menu and clipboard management
- System tray and global shortcuts
Renderer Process
Core Responsibilities
// Renderer Process Typical Responsibilities Example
// 1. UI Rendering and Interaction
document.getElementById('btn').addEventListener('click', () => {
// Handle User Interaction
});
// 2. Web Technology Stack Development
import React from 'react'; // Use Modern Web Frameworks
import axios from 'axios'; // Make Network Requests
// 3. Communicate with Main Process via IPC
const { ipcRenderer } = require('electron');
ipcRenderer.invoke('read-file', '/path/to/file');Limitations and Constraints
- Restricted by browser security sandbox
- Cannot directly access system-level APIs
- Requires IPC to communicate with the main process for privileged operations
Process Model Comparison
| Feature | Main Process | Renderer Process |
|---|---|---|
| Process Count | 1 (can create multiple windows) | One per window |
| System API Access | Full access | Restricted access |
| Crash Impact | Crashes entire application | Affects only the current window |
| Memory Management | Requires careful management | Can be independently reclaimed |
| Typical Use | System integration, window management | UI rendering, business logic |
Inter-Process Communication (IPC) Mechanism
ipcMain and ipcRenderer
Main Process Event Listening
// main.js
const { ipcMain } = require('electron');
// Synchronous Communication
ipcMain.on('sync-message', (event, arg) => {
event.returnValue = `Synchronous Reply: ${arg}`;
});
// Asynchronous Communication
ipcMain.handle('async-message', async (event, arg) => {
const result = await someAsyncOperation(arg);
return `Asynchronous Reply: ${result}`;
});Renderer Process Event Sending
// renderer.js
const { ipcRenderer } = require('electron');
// Synchronous Call
const reply = ipcRenderer.sendSync('sync-message', 'ping');
console.log(reply); // "Synchronous Reply: ping"
// Asynchronous Call
ipcRenderer.invoke('async-message', 'pong').then((result) => {
console.log(result); // "Asynchronous Reply: pong"
});Communication Performance Optimization Strategies
- Batch Data Transfer: Reduce cross-process call frequency.
- Shared Memory: Expose limited APIs via
contextBridge. - Communication Protocol Optimization: Use binary format instead of JSON.
- Channel Reuse: Avoid frequent creation/destruction of communication channels.
Chromium and Node.js Integration
Key Integration Technologies
V8 Engine Sharing:
- Chromium and Node.js use the same V8 instance.
- Reduces memory usage and context-switching overhead.
Node.js Integration Mode:
// BrowserWindow Configuration Example
new BrowserWindow({
webPreferences: {
nodeIntegration: true, // Enable Node.js Integration
contextIsolation: false // Disable Context Isolation (Not Recommended)
}
});Modern Security Practices:
// Recommended Security Configuration
new BrowserWindow({
webPreferences: {
nodeIntegration: false, // Disable Automatic Integration
contextIsolation: true, // Enable Context Isolation
preload: path.join(__dirname, 'preload.js') // Use Preload Script
}
});Context Isolation and Preload Scripts
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
// Safely Expose APIs
contextBridge.exposeInMainWorld('electronAPI', {
readFile: (path) => ipcRenderer.invoke('read-file', path)
});
// Use in Renderer Process
window.electronAPI.readFile('/path/to/file');Native Modules and System API Access
Development Steps Example
Create C++ Module:
// native-addon.cc
#include <napi.h>
napi_value Hello(napi_env env, napi_callback_info info) {
napi_value result;
napi_create_string_utf8(env, "Hello from C++", NAPI_AUTO_LENGTH, &result);
return result;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) {
napi_property_descriptor desc = {"hello", 0, Hello, 0, 0, 0, napi_default, 0};
napi_define_properties(env, exports, 1, &desc);
}Compilation Configuration:
# binding.gyp
{
"targets": [{
"target_name": "native-addon",
"sources": ["native-addon.cc"]
}]
}Electron Integration:
# Recompile with electron-rebuild
npx electron-rebuild -f -w native-addonSystem API Access Capabilities
| System Function | Implementation Method | Example Use Case |
|---|---|---|
| File System Operations | Node.js fs module | Read/write configuration files |
| Native Dialogs | electron.dialog module | File selection/saving |
| System Notifications | electron.notification module | Desktop notifications |
| Hardware Device Access | Native modules (C++) | USB device communication |
| Window Management | electron.BrowserWindow | Multi-window control |
Application Packaging and Distribution
Packaging Tool Comparison
| Tool | Features | Use Case |
|---|---|---|
| electron-builder | Comprehensive, supports auto-updates | Commercial application distribution |
| electron-packager | Lightweight, highly configurable | Custom packaging needs |
electron-builder Configuration Example
// package.json
{
"build": {
"appId": "com.example.app",
"productName": "MyApp",
"directories": {
"output": "dist"
},
"files": [
"dist/**/*",
"package.json"
],
"win": {
"target": ["nsis"],
"icon": "build/icon.ico"
},
"mac": {
"target": ["dmg"],
"icon": "build/icon.icns"
},
"linux": {
"target": ["AppImage"],
"icon": "build/icon.png"
}
}
}Packaging Process Details
- Code Packaging:
- Use Webpack/Rollup to bundle frontend resources.
- Copy Node.js dependencies to the build directory.
- Resource Handling:
- Convert icons (ICO/ICNS/PNG).
- Compile binary dependencies.
- Installer Generation:
- Windows: NSIS/Squirrel installers.
- macOS: DMG/App Store packages.
- Linux: AppImage/DEB/RPM.
Auto-Update Implementation
// Main Process Auto-Update Code
const { autoUpdater } = require('electron');
autoUpdater.setFeedURL({
provider: 'github',
repo: 'my-app',
owner: 'my-org',
private: false
});
autoUpdater.checkForUpdatesAndNotify();
// Renderer Process Listens for Update Events
window.electronAPI.onUpdateAvailable(() => {
// Prompt User to Restart for Update
});Distribution Channel Strategies
- Official Website Downloads:
- Provide installers for each platform.
- Integrate auto-update services.
- App Store Distribution:
- macOS App Store.
- Microsoft Store.
- Linux distribution repositories.
- Enterprise Distribution:
- Internal server deployment.
- Private update server configuration.
Through the above architectural design and toolchain support, Electron enables an efficient development model that combines web technologies with native system capabilities, allowing developers to build cross-platform desktop applications using familiar web technology stacks.
Comprehensive Case Study: Simple Notepad Application
Requirements Analysis
Implement a simple notepad:
- Main window displays a text input box.
- Save and read files.
- Use main and renderer processes for communication.
Implementation Code Breakdown
main.js
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const fs = require('fs').promises;
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
win.loadFile('index.html');
}
app.whenReady().then(createWindow);
ipcMain.handle('save-file', async (event, content) => {
const { filePath } = await dialog.showSaveDialog({
defaultPath: 'note.txt',
filters: [{ name: 'Text Files', extensions: ['txt'] }],
});
if (filePath) {
await fs.writeFile(filePath, content);
return filePath;
}
return null;
});
ipcMain.handle('load-file', async () => {
const { filePaths } = await dialog.showOpenDialog({
filters: [{ name: 'Text Files', extensions: ['txt'] }],
});
if (filePaths && filePaths.length > 0) {
const content = await fs.readFile(filePaths[0], 'utf8');
return content;
}
return null;
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
saveFile: (content) => ipcRenderer.invoke('save-file', content),
loadFile: () => ipcRenderer.invoke('load-file'),
});index.html
<!DOCTYPE html>
<html>
<head>
<title>Simple Notepad</title>
</head>
<body>
<h1>Simple Notepad</h1>
<textarea id="content" rows="10" cols="50"></textarea>
<br>
<button onclick="saveFile()">Save</button>
<button onclick="loadFile()">Load</button>
<script>
function saveFile() {
const content = document.getElementById('content').value;
window.electronAPI.saveFile(content).then(filePath => {
if (filePath) alert('File saved: ' + filePath);
});
}
function loadFile() {
window.electronAPI.loadFile().then(content => {
if (content) document.getElementById('content').value = content;
});
}
</script>
</body>
</html>package.json
{
"name": "simple-notepad",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^28.2.2"
}
}Run
npm startAnalysis
- Main Process: Handles file saving and reading.
- Renderer Process: Displays UI and handles user interaction.
- IPC: Facilitates secure communication via
preload.js.
Advanced Techniques and Considerations
Performance Optimization Tips
- Reduce IPC Calls: Batch data transfers.
- Cache Resources: Store static files in
assets/. - Asynchronous Operations: Use
fs.promisesfor file operations.
Debugging and Error Handling
- Developer Tools: Right-click window and select “Inspect”.
- Logging:
console.log('Debug:', data);- Error Handling:
try {
await fs.writeFile(filePath, content);
} catch (error) {
console.error('Error:', error);
}



