Electron 是什么
Electron 用 Chromium(渲染界面)+ Node.js(系统能力)构建跨平台桌面应用。前端技术栈(HTML/CSS/JS、Vue、React)可以直接用来做桌面软件,VS Code、Slack、飞书桌面版都是它的代表。
一、理解三个进程
Electron 应用由三部分协作:
| 部分 |
作用 |
能访问什么 |
| 主进程(main) |
应用入口,创建窗口、管理系统事件 |
Node.js 全部能力(文件系统、网络、系统 API) |
| 渲染进程(renderer) |
页面 UI,运行在 Chromium 里 |
DOM、一部分受限 Node 能力 |
| 预加载脚本(preload) |
在渲染进程加载前执行,桥接主进程与渲染进程 |
通过 contextBridge 暴露安全 API |
安全模型:渲染进程不能直接碰 Node.js API,所有系统操作都要通过 IPC(进程间通信)找主进程完成。
二、初始化项目
1 2 3
| mkdir my-electron-app && cd my-electron-app npm init -y npm install --save-dev electron
|
目录结构:
1 2 3 4 5
| my-electron-app/ ├── main.js # 主进程 ├── preload.js # 预加载脚本 ├── index.html # 渲染进程页面 └── package.json
|
package.json 里声明入口:
1 2 3 4 5 6
| { "main": "main.js", "scripts": { "start": "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 29 30 31 32
| const { app, BrowserWindow, ipcMain } = require('electron') const path = require('path')
function createWindow() { const win = new BrowserWindow({ width: 1000, height: 700, webPreferences: { contextIsolation: true, preload: path.join(__dirname, 'preload.js') } })
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() })
|
四、IPC 通信:渲染进程请求主进程做事
场景:页面里点按钮,读取本机文件内容。
主进程监听(main.js 追加):
1 2 3 4 5 6 7 8 9 10
| ipcMain.handle('read-file', async (event, filePath) => { const fs = require('fs/promises') try { const content = await fs.readFile(filePath, 'utf-8') return { ok: true, content } } catch (e) { return { ok: false, message: e.message } } })
|
preload 脚本暴露安全 API(preload.js):
1 2 3 4 5 6 7
| const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('api', { readFile: (filePath) => ipcRenderer.invoke('read-file', filePath) })
|
渲染进程调用(index.html):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> </head> <body> <button id="btn">读取文件</button> <pre id="result"></pre>
<script> document.getElementById('btn').addEventListener('click', async () => { const res = await window.api.readFile('/etc/hosts') document.getElementById('result').textContent = res.ok ? res.content : '读取失败:' + res.message }) </script> </body> </html>
|
通信模式总结:
- 渲染 → 主(请求-响应):ipcRenderer.invoke + ipcMain.handle
- 渲染 → 主(单向通知):ipcRenderer.send + ipcMain.on
- 主 → 渲染(推送):win.webContents.send + ipcRenderer.on(如后台任务进度)
主进程主动推送示例:
1 2 3 4 5 6 7
| function sendProgress(win, percent) { win.webContents.send('progress', percent) }
|
五、安全配置清单(生产环境必看)
1 2 3 4 5 6
| webPreferences: { contextIsolation: true, nodeIntegration: false, preload: path.join(__dirname, 'preload.js'), sandbox: true }
|
- 不要在渲染进程里 require(‘fs’)、require(‘child_process’),一律通过 preload 白名单走 IPC;
- preload 只暴露必要的白名单方法,不要直接暴露整个 ipcRenderer;
- 加载外部页面时(win.loadURL)要格外小心 XSS,本地页面也要转义用户输入。
六、打包发布
推荐用 electron-builder:
1
| npm install --save-dev electron-builder
|
package.json 追加:
1 2 3 4 5 6 7 8 9 10 11 12
| { "build": { "appId": "com.example.myapp", "productName": "MyApp", "files": ["main.js", "preload.js", "index.html"], "win": { "target": "nsis" }, "mac": { "target": "dmg" } }, "scripts": { "build": "electron-builder" } }
|
产物在 dist/ 目录:Windows 是 .exe 安装包,macOS 是 .dmg。
常见坑
- 渲染进程拿不到 Node 模块:先检查 contextIsolation/nodeIntegration 配置,再检查是否通过 preload 暴露;
- IPC 通道名拼错:invoke 和 handle 的名字必须完全一致;
- 打包后白屏:检查 files 配置是否把页面和 preload 打进去了;
- macOS 打包签名:正式分发需要 Apple Developer 证书,本地测试可跳过签名。
总结
Electron 开发的关键是先理解三进程模型,再遵守”一切系统操作走 IPC + preload 白名单”的安全规范。按”主进程建窗口 → preload 暴露 API → 渲染进程调用”的路径,就能搭出规范的桌面应用。