一、项目结构规范
一个生产级别的 Electron 项目目录结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| my-app/ ├── package.json ├── main.js # 主进程入口 ├── preload.js # 预加载脚本 ├── src/ # 渲染进程源码 │ ├── index.html │ ├── renderer.js │ └── styles.css ├── build/ # 构建资源 │ ├── icon.icns # macOS 图标 │ ├── icon.ico # Windows 图标 │ └── icon.png # Linux 图标 ├── electron-builder.yml # 打包配置 └── dev-app-update.yml # 开发环境自动更新配置
|
二、打包(electron-builder)
安装
1
| npm install electron-builder --save-dev
|
配置
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 33 34 35 36 37 38 39
| appId: com.example.myapp productName: MyApp copyright: Copyright © 2024
directories: output: release buildResources: build
files: - main.js - preload.js - src/**/* - node_modules/**/*
mac: category: public.app-category.utilities icon: build/icon.icns target: - dmg - zip
win: icon: build/icon.ico target: - target: nsis arch: - x64
nsis: oneClick: false allowToChangeInstallationDirectory: true createDesktopShortcut: true
linux: icon: build/icon.png target: - AppImage - deb
|
脚本配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| { "scripts": { "pack": "electron-builder --dir", "dist": "electron-builder", "dist:win": "electron-builder --win", "dist:mac": "electron-builder --mac", "dist:linux": "electron-builder --linux" }, "build": { "appId": "com.example.myapp", "extends": null, "files": ["main.js", "preload.js", "src/**/*"] } }
|
.gitignore
1 2 3 4
| node_modules/ dist/ release/ *.log
|
打包命令
1 2 3 4 5 6 7 8 9 10
| npm run dist
npm run dist:win npm run dist:mac npm run dist:linux
npm run pack
|
三、自动更新
主进程配置
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
| const { autoUpdater } = require('electron-updater');
function setupAutoUpdater() { autoUpdater.checkForUpdates();
autoUpdater.on('update-available', (info) => { mainWindow.webContents.send('update-available', info); });
autoUpdater.on('download-progress', (progress) => { mainWindow.webContents.send('update-progress', progress); });
autoUpdater.on('update-downloaded', () => { autoUpdater.quitAndInstall(); });
autoUpdater.on('error', (err) => { console.error('Update error:', err); }); }
ipcMain.handle('install-update', () => { autoUpdater.quitAndInstall(); });
|
electron-builder 配置
1 2 3 4 5
| publish: provider: generic url: https://releases.example.com/ channel: latest
|
1 2 3
| provider: generic url: http://localhost:8080
|
渲染进程
1 2 3 4 5 6 7 8 9
| contextBridge.exposeInMainWorld('api', { onUpdateAvailable: (callback) => ipcRenderer.on('update-available', (_, info) => callback(info)), onUpdateProgress: (callback) => ipcRenderer.on('update-progress', (_, p) => callback(p)), installUpdate: () => ipcRenderer.invoke('install-update'), });
|
四、安全最佳实践
1. 启用沙箱和上下文隔离
1 2 3 4 5 6 7 8
| const win = new BrowserWindow({ webPreferences: { nodeIntegration: false, contextIsolation: true, sandbox: true, preload: path.join(__dirname, 'preload.js'), }, });
|
2. 限制预加载脚本暴露的 API
1 2 3 4 5 6 7 8 9 10
| contextBridge.exposeInMainWorld('api', { readFile: (path) => ipcRenderer.invoke('read-file', path), writeFile: (path, data) => ipcRenderer.invoke('write-file', path, data), });
contextBridge.exposeInMainWorld('electron', { ipcRenderer: ipcRenderer, });
|
3. 验证 IPC 参数
1 2 3 4 5 6 7 8 9
| const allowedPaths = [app.getPath('documents'), app.getPath('downloads')];
ipcMain.handle('read-file', async (event, filePath) => { if (!allowedPaths.some(p => filePath.startsWith(p))) { throw new Error('不允许访问该路径'); } return fs.readFileSync(filePath, 'utf-8'); });
|
4. 禁止加载远程内容
1 2 3
| win.loadFile('index.html'); win.loadURL('file://index.html'); win.loadURL('https://external.com');
|
5. 禁用未使用的功能
1 2 3 4 5 6 7 8
| win.webContents.session.webRequest.onHeadersReceived((details, callback) => { callback({ responseHeaders: { ...details.responseHeaders, 'Content-Security-Policy': ["default-src 'self'"], }, }); });
|
五、开发效率
开发模式与热重载
1
| npm install electron-reload --save-dev
|
1 2 3 4 5 6
| if (process.env.NODE_ENV === 'development') { require('electron-reload')(__dirname, { electron: path.join(__dirname, 'node_modules', '.bin', 'electron'), }); }
|
主进程与渲染进程分离开发
1 2 3 4 5 6 7 8
| { "scripts": { "dev:renderer": "vite", "dev:main": "electron .", "dev": "concurrently \"npm run dev:renderer\" \"npm run dev:main\"" } }
|
Electron 集成 Vite
使用 electron-vite 可以统一管理主进程和渲染进程的构建:
1
| npm create @quick-start/electron my-app -- --template vue
|
1 2 3 4 5 6
| my-app/ ├── electron/ │ ├── main/ # 主进程 │ └── preload/ # 预加载脚本 ├── src/ # 渲染进程(Vue/React) └── electron.vite.config.ts
|
六、性能优化
1. 窗口懒加载
1 2 3 4 5 6 7
| const win = new BrowserWindow({ show: false }); win.loadFile('index.html');
win.once('ready-to-show', () => { win.show(); });
|
2. 减少渲染进程内存占用
1 2 3 4 5 6 7 8 9
| win.on('closed', () => { win = null; });
mainWin.on('blur', () => { });
|
3. 使用 GPU 加速
1 2
| app.disableHardwareAcceleration();
|
4. 分析性能
1 2 3
|
console.log(`内存使用:${process.getSystemMemoryInfo().free / 1024 / 1024} MB`);
|
七、常见问题
Q1: 打包后的应用打不开
Q2: macOS 下应用被提示损坏
1 2 3 4
| sudo xattr -dr com.apple.quarantine /Applications/MyApp.app
|
Q3: Windows 下安装包被杀毒软件误报
1 2 3 4 5 6 7 8 9
| nsis: oneClick: false perMachine: false
win: certificateFile: cert.p12 certificatePassword: password
|
Q4: 自动更新不生效
Q5: 应用图标不显示
八、推荐学习路径
- 掌握主进程和渲染进程的架构(入门与架构篇)
- 熟悉 BrowserWindow、Menu、Tray、IPC 等核心 API
- 配置 electron-builder 打包
- 配置自动更新
- 了解安全模型和性能优化
- 使用 electron-vite 搭建现代化开发环境