一、Electron 是什么
Electron 是一个使用 Web 技术(HTML、CSS、JavaScript)构建跨平台桌面应用的框架。它由 GitHub 于 2013 年创建(最初叫 Atom Shell),目前由 OpenJS Foundation 维护。
用 Web 技术写桌面应用
1
| Electron = Chromium(渲染) + Node.js(系统能力) + 原生 API(窗口/菜单/托盘)
|
这意味着:如果你会写前端,就能写桌面应用。代码写一次,打包成 Windows、macOS 和 Linux 三个平台的原生安装包。
知名产品
- VS Code(最著名的 Electron 应用)
- Slack、Discord、WhatsApp Desktop
- Figma(桌面版)
- Notion、Obsidian
- Postman、Typora
- GitHub Desktop
Electron vs 其他方案
| 方案 | 技术栈 | 体积 | 性能 | 生态 |
|---|
| Electron | HTML/CSS/JS + Node.js | 较大(~150MB) | 较高内存 | 最成熟 |
| Tauri | HTML/CSS/JS + Rust | 极小(~5MB) | 低内存 | 成长中 |
| Flutter | Dart | 中等 | 好 | 移动端为主 |
| Qt | C++ | 小 | 最好 | 学习成本高 |
| NW.js | HTML/CSS/JS + Node.js | 大 | 同 Electron | 社区较小 |
二、核心架构
Electron 应用由两种进程组成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| ┌──────────────────────────────────────────┐ │ 主进程(Main Process) │ │ - 启动应用、创建窗口 │ │ - 访问原生 API(Menu、Tray、dialog) │ │ - 控制应用生命周期 │ │ - 只能有一个主进程 │ └────────────┬─────────────────────────────┘ │ IPC (ipcMain / ipcRenderer) │ ┌────────┴────────┐ │ 预加载脚本 │ │ preload.js │ │ (contextBridge)│ └────────┬────────┘ │ ┌────────┴────────┐ ┌─────────────────┐ │ 渲染进程 1 │ │ 渲染进程 2 │ │ Renderer │ │ Renderer │ │ (Chromium) │ │ (Chromium) │ │ 每个窗口一个 │ │ 每个窗口一个 │ └─────────────────┘ └─────────────────┘
|
主进程(Main Process)
- 应用入口,运行在 Node.js 环境中
- 负责创建窗口、管理应用生命周期
- 可以调用所有 Node.js API 和 Electron 原生模块
- 每个应用只有一个主进程(
main.js 或 main.ts)
渲染进程(Renderer Process)
- 每个
BrowserWindow 对应一个渲染进程 - 运行在 Chromium 沙箱中,类似浏览器环境
- 可以访问 DOM、CSS、
window 对象 - 默认不能直接访问 Node.js API(出于安全考虑)
预加载脚本(Preload Script)
- 在主进程和渲染进程之间架桥
- 使用
contextBridge 安全地暴露有限 API 给渲染进程 - 运行在渲染进程中,但可以访问部分 Node.js API
IPC(进程间通信)
主进程和渲染进程之间通过 ipcMain 和 ipcRenderer 通信:
1 2 3 4 5 6 7 8 9
| 渲染进程 主进程 │ │ │── ipcRenderer.invoke ──→ ipcMain.handle │ │ │←── Promise resolve ──── return value │ │ │── ipcRenderer.send ──→ ipcMain.on │ │ │←── event.reply ──────── webContents.send
|
三、快速开始
创建项目
1 2 3
| mkdir my-app && cd my-app npm init -y npm install electron
|
入口文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| const { app, BrowserWindow } = require('electron');
function createWindow() { const win = new BrowserWindow({ width: 1024, height: 768, webPreferences: { preload: path.join(__dirname, 'preload.js'), nodeIntegration: false, contextIsolation: true, }, });
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(); });
|
HTML 页面
1 2 3 4 5 6 7 8 9 10 11
| <!DOCTYPE html> <html> <head><meta charset="UTF-8"><title>Electron App</title></head> <body> <h1>Hello Electron</h1> <button id="btn">版本信息</button> <p id="info"></p> <script src="renderer.js"></script> </body> </html>
|
预加载脚本
1 2 3 4 5 6 7
| const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', { getVersion: () => ipcRenderer.invoke('get-version'), getPlatform: () => ipcRenderer.invoke('get-platform'), });
|
渲染进程
1 2 3 4 5 6
| document.getElementById('btn').addEventListener('click', async () => { const version = await window.electronAPI.getVersion(); const platform = await window.electronAPI.getPlatform(); document.getElementById('info').textContent = `${platform} v${version}`; });
|
主进程处理 IPC
1 2 3 4 5 6 7 8 9 10
| const { ipcMain } = require('electron');
ipcMain.handle('get-version', () => { return process.versions.electron; });
ipcMain.handle('get-platform', () => { return process.platform; });
|
启动
1 2 3 4 5 6 7
| { "main": "main.js", "scripts": { "start": "electron ." } }
|
四、上下文隔离与安全
Electron 的安全模型经历了几个阶段的变化。新项目的推荐配置:
1 2 3 4 5 6 7 8 9
| const win = new BrowserWindow({ webPreferences: { nodeIntegration: false, contextIsolation: true, preload: path.join(__dirname, 'preload.js'), sandbox: true, webSecurity: true, }, });
|
1 2 3 4 5 6 7
| contextBridge.exposeInMainWorld('api', { readFile: (path) => ipcRenderer.invoke('read-file', path), });
|
五、应用生命周期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const { app } = require('electron');
app.on('ready', () => { });
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });
app.on('activate', () => { });
app.on('before-quit', () => { });
app.whenReady().then(() => { });
|
六、常见问题
Q1: Electron 应用体积为什么这么大
Electron 内置了完整 Chromium 和 Node.js(约 150MB)。可选的优化方式:
- 使用
electron-builder 的 asar 压缩 - 删除不需要的 Chromium 组件
- 使用 NSIS(Windows)/ DMG(macOS)压缩
Q2: 和浏览器的区别
| 能力 | 浏览器 | Electron |
|---|
| DOM 操作 | ✅ | ✅ |
| 文件系统 | ❌ | ✅(通过 IPC + 主进程) |
| 原生窗口 | ❌ | ✅ BrowserWindow |
| 系统托盘 | ❌ | ✅ Tray |
| 通知 | ✅ Web Notification | ✅ Notification API |
| 自动更新 | ❌ | ✅ autoUpdater |
| 菜单栏 | ❌ | ✅ Menu |
Q3: contextIsolation 是什么
contextIsolation: true 将渲染进程的 JavaScript 上下文与预加载脚本的上下文隔离,防止渲染进程直接访问 Node.js API。这是 Electron 12+ 的默认值,必须保持开启。
七、推荐学习路径
- 理解主进程与渲染进程的架构和 IPC 通信
- 创建一个基本的窗口应用(本文)
- 掌握 BrowserWindow 的配置和生命周期
- 使用 ipcMain/ipcRenderer 实现进程通信
- 了解 contextBridge 的安全暴露模式
- 学习 Menu、Tray、dialog 等原生 API