一、环境准备
1.1 Node.js 版本选择
推荐版本:Node.js 18.x 或 20.x LTS(长期支持版本)
- 查看 Electron 官网 兼容性要求,中文官方文档
- 查看当前版本:
node -v和npm -v
重要说明:Electron 内置 Node.js 与系统 Node.js 的关系
-
系统 Node.js(开发时用)
- 用于运行 npm 命令、安装依赖、执行打包工具
- 用于开发调试、运行构建脚本
- 这就是你电脑上安装的 Node.js
-
Electron 内置 Node.js(运行时用)
- Electron 内部集成了特定版本的 Node.js
- 应用运行时使用的是 Electron 内置的 Node.js,不是系统的 Node.js
- 可通过
process.versions.node查看 Electron 内置的 Node 版本 - 不同 Electron 版本内置的 Node.js 版本不同
-
为什么两个都需要?
- 系统 Node.js:开发阶段使用(npm、构建、打包)
- 内置 Node.js:应用运行时使用(用户电脑无需安装 Node.js)
示例:
# 系统 Node 版本
$ node -v
v20.10.0
# Electron 应用运行时在控制台输出
> console.log(process.versions.node)
18.17.1 # Electron 内置的 Node 版本(可能与系统不同)
安装方式:
- 官网下载:nodejs.org/
- nvm 管理多版本:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash nvm install 20 nvm use 20
二、项目初始化
2.1 创建项目
mkdir my-electron-app
cd my-electron-app
npm init -y
2.2 安装核心依赖(必需)
# Electron 运行时(开发依赖)
npm install electron --save-dev
# 打包工具(开发依赖)
npm install electron-builder --save-dev
国内镜像加速(可选):
# 创建 .npmrc 文件
echo "electron_mirror=https://npmmirror.com/mirrors/electron/" >> .npmrc
echo "registry=https://registry.npmmirror.com" >> .npmrc
三、创建项目文件
3.1 项目结构
my-electron-app/
├── package.json # 项目配置
├── main.js # 主进程入口
├── preload.js # 预加载脚本
└── index.html # 渲染进程页面
3.2 主进程文件 main.js
const { app, BrowserWindow } = require('electron');
const path = require('path');
// 创建窗口
function createWindow() {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false, // 安全考虑,不直接暴露 Node.js
contextIsolation: true, // 启用上下文隔离
}
});
mainWindow.loadFile('index.html');
// 开发时打开调试工具(可选)
// mainWindow.webContents.openDevTools();
}
// 应用就绪时创建窗口
app.whenReady().then(() => {
createWindow();
// macOS:点击 Dock 图标时重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
// 所有窗口关闭时退出(macOS 除外)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
3.3 预加载脚本 preload.js
const { contextBridge, ipcRenderer } = require('electron');
// 向渲染进程暴露安全的 API
contextBridge.exposeInMainWorld('electronAPI', {
// 示例:获取版本信息
getVersion: () => process.versions.electron,
// 进程间通信示例
sendMessage: (channel, data) => {
// 白名单验证
const validChannels = ['message-to-main'];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
// 异步调用示例
invokeAction: (channel, data) => {
const validChannels = ['get-data', 'save-file'];
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, data);
}
}
});
3.4 页面文件 index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>你好世界</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
h1 {
font-size: 48px;
margin-bottom: 20px;
}
.info {
background: rgba(255, 255, 255, 0.1);
padding: 20px 40px;
border-radius: 10px;
backdrop-filter: blur(10px);
}
.info p {
margin: 10px 0;
}
</style>
</head>
<body>
<h1>🎉 你好世界!</h1>
<div class="info">
<p>欢迎使用 Electron</p>
<p>Electron: <span id="electron-version"></span></p>
<p>Node.js: <span id="node-version"></span></p>
<p>Chrome: <span id="chrome-version"></span></p>
</div>
<script>
document.getElementById('electron-version').textContent = process.versions.electron;
document.getElementById('node-version').textContent = process.versions.node;
document.getElementById('chrome-version').textContent = process.versions.chrome;
</script>
</body>
</html>
四、package.json 完整配置
{
// ========== 基础信息 ==========
"name": "my-electron-app", // 应用名称(小写、无空格)
"version": "1.0.0", // 版本号(semver 格式)
"description": "我的 Electron 应用", // 应用描述
"main": "main.js", // 主进程入口文件(必需)
"author": "你的名字 <your@email.com>", // 作者信息
"license": "MIT", // 开源协议
"private": true, // 防止意外发布到 npm
// ========== 运行脚本 ==========
"scripts": {
"start": "electron .", // 启动应用
"dev": "electron . --enable-logging", // 开发模式(显示日志)
"pack": "electron-builder --dir", // 打包但不生成安装包
"dist": "electron-builder", // 打包当前平台
"dist:mac": "electron-builder --mac", // 打包 macOS
"dist:win": "electron-builder --win", // 打包 Windows
"dist:linux": "electron-builder --linux", // 打包 Linux
"dist:all": "electron-builder -mwl" // 打包所有平台
},
// ========== 开发依赖 ==========
"devDependencies": {
"electron": "^28.0.0", // Electron 运行时
"electron-builder": "^24.9.1" // 打包工具
},
// ========== 打包配置 ==========
"build": {
// --- 通用配置 ---
"appId": "com.example.myapp", // 应用唯一标识(反向域名)
"productName": "我的应用", // 应用显示名称(可包含中文和空格)
"copyright": "Copyright © 2024 Your Name", // 版权信息
"asar": true, // 打包为 asar 归档(推荐)
"compression": "normal", // 压缩级别:store/normal/maximum
// --- 目录配置 ---
"directories": {
"output": "dist", // 输出目录
"buildResources": "build" // 构建资源目录(图标等)
},
// --- 文件配置 ---
"files": [
"**/*", // 包含所有文件
"!**/*.ts", // 排除 TypeScript 源文件
"!*.code-workspace", // 排除 VS Code 工作区
"!src/", // 排除源码目录(如已编译)
"!node_modules/**/*", // 排除所有 node_modules
"node_modules/需要的包/**/*" // 只包含需要的依赖
],
// --- 额外资源 ---
"extraResources": [
{
"from": "assets/", // 源目录
"to": "assets", // 目标目录
"filter": ["**/*"] // 过滤规则
}
],
// ========== macOS 配置 ==========
"mac": {
"icon": "build/icon.icns", // 图标文件(512x512)
"category": "public.app-category.utilities", // 应用分类
"target": [
{
"target": "dmg", // DMG 镜像
"arch": ["x64", "arm64"] // 支持 Intel 和 Apple Silicon
},
{
"target": "zip", // ZIP 压缩包
"arch": ["universal"] // 通用二进制
}
],
"type": "distribution", // 分发类型:distribution/development
"hardenedRuntime": true, // 强化运行时(公证需要)
"gatekeeperAssess": false, // Gatekeeper 评估
"entitlements": "build/entitlements.mac.plist", // 权限配置
"entitlementsInherit": "build/entitlements.mac.plist", // 继承权限
"provisioningProfile": "build/profile.provisionprofile", // 描述文件
"identity": "Developer ID Application: Your Name (TEAM)", // 签名身份
"minimumSystemVersion": "10.13.0" // 最低系统版本
},
// --- macOS DMG 配置 ---
"dmg": {
"title": "${productName} ${version}", // DMG 标题
"icon": "build/icon.icns", // DMG 图标
"background": "build/background.png", // 背景图片(540x380)
"window": {
"width": 540, // 窗口宽度
"height": 380 // 窗口高度
},
"contents": [
{
"x": 140, // 应用图标 x 坐标
"y": 180 // 应用图标 y 坐标
},
{
"x": 400, // Applications 快捷方式 x 坐标
"y": 180, // Applications 快捷方式 y 坐标
"type": "link",
"path": "/Applications"
}
]
},
// --- macOS PKG 配置 ---
"pkg": {
"license": "LICENSE.txt", // 许可证文件
"installLocation": "/Applications", // 安装位置
"allowAnywhere": false, // 允许安装到任意位置
"allowCurrentUserHome": false, // 允许安装到用户主目录
"allowRootDirectory": false // 允许安装到根目录
},
// ========== Windows 配置 ==========
"win": {
"icon": "build/icon.ico", // 图标文件(256x256)
"target": [
{
"target": "nsis", // NSIS 安装包
"arch": ["x64", "ia32"] // 64位和32位
},
{
"target": "portable", // 便携版
"arch": ["x64"]
},
{
"target": "zip", // ZIP 压缩包
"arch": ["x64"]
}
],
"publisherName": "Your Company", // 发布者名称
"verifyUpdateCodeSignature": true, // 验证更新签名
"certificateFile": "path/to/cert.pfx", // 证书文件
"certificatePassword": "password", // 证书密码(建议用环境变量)
"signingHashAlgorithms": ["sha256"], // 签名算法
"signDlls": true, // 签名 DLL 文件
"rfc3161TimeStampServer": "http://timestamp.digicert.com", // 时间戳服务器
"requestedExecutionLevel": "asInvoker" // 执行级别:asInvoker/highestAvailable/requireAdministrator
},
// --- Windows NSIS 配置 ---
"nsis": {
"oneClick": false, // 一键安装(true=无选项)
"allowToChangeInstallationDirectory": true, // 允许选择安装目录
"allowElevation": true, // 允许权限提升
"installerIcon": "build/installer.ico", // 安装程序图标
"uninstallerIcon": "build/uninstaller.ico", // 卸载程序图标
"installerHeaderIcon": "build/icon.ico", // 安装程序头部图标
"createDesktopShortcut": true, // 创建桌面快捷方式
"createStartMenuShortcut": true, // 创建开始菜单快捷方式
"shortcutName": "我的应用", // 快捷方式名称
"perMachine": false, // 为所有用户安装(需要管理员权限)
"runAfterFinish": true, // 安装完成后运行
"deleteAppDataOnUninstall": false, // 卸载时删除应用数据
"menuCategory": false, // 开始菜单分类(false=不创建子菜单)
"artifactName": "${productName}-Setup-${version}.${ext}", // 安装包文件名
"uninstallDisplayName": "${productName}", // 卸载程序显示名称
"license": "LICENSE.txt", // 许可证文件
"include": "build/installer.nsh", // 自定义 NSIS 脚本
"multiLanguageInstaller": true, // 多语言安装程序
"language": "2052", // 语言 ID(2052=简体中文)
"warningsAsErrors": true // 警告视为错误
},
// --- Windows 便携版配置 ---
"portable": {
"artifactName": "${productName}-Portable-${version}.${ext}"
},
// ========== Linux 配置 ==========
"linux": {
"icon": "build/icon.png", // 图标文件(512x512)
"category": "Utility", // 应用分类
"target": [
{
"target": "AppImage", // AppImage 格式
"arch": ["x64", "arm64"]
},
{
"target": "deb", // Debian 包
"arch": ["x64", "arm64"]
},
{
"target": "rpm", // RedHat 包
"arch": ["x64"]
},
{
"target": "snap", // Snap 包
"arch": ["x64"]
}
],
"executableName": "my-app", // 可执行文件名
"synopsis": "简短描述", // 简短描述(一行)
"description": "详细描述\n可以多行", // 详细描述
"maintainer": "maintainer@example.com", // 维护者邮箱
"vendor": "Your Company", // 供应商名称
"desktop": { // 桌面文件配置
"Name": "我的应用",
"GenericName": "应用通用名称",
"Comment": "应用描述",
"Type": "Application",
"Terminal": false,
"Categories": "Utility;Development;",
"MimeType": "x-scheme-handler/myapp;",
"StartupWMClass": "my-electron-app"
}
},
// --- Linux AppImage 配置 ---
"appImage": {
"license": "LICENSE.txt", // 许可证文件
"artifactName": "${productName}-${version}-${arch}.${ext}",
"systemIntegration": "ask" // 系统集成:ask/doNotAsk
},
// --- Linux Snap 配置 ---
"snap": {
"grade": "stable", // 等级:stable/devel
"confinement": "strict", // 限制:strict/devmode/classic
"summary": "简短描述(最多78字符)",
"plugs": [ // 插件权限
"default",
"home",
"network",
"removable-media"
]
},
// --- Linux Deb 配置 ---
"deb": {
"depends": [ // 依赖包
"libgtk-3-0",
"libnotify4",
"libnss3",
"libxss1"
],
"recommends": [], // 推荐包
"suggests": [], // 建议包
"priority": "optional", // 优先级:required/important/standard/optional/extra
"compression": "xz", // 压缩方式:gzip/xz/bzip2
"afterInstall": "build/deb-postinst.sh", // 安装后脚本
"afterRemove": "build/deb-postrm.sh" // 卸载后脚本
},
// ========== 发布配置 ==========
"publish": [
{
"provider": "github", // 发布平台:github/s3/generic
"owner": "your-username", // GitHub 用户名
"repo": "your-repo", // 仓库名
"token": "${GH_TOKEN}", // GitHub Token(环境变量)
"private": false, // 私有仓库
"releaseType": "release" // 发布类型:draft/prerelease/release
},
{
"provider": "generic", // 通用服务器
"url": "https://example.com/releases", // 更新服务器地址
"channel": "latest" // 更新通道
}
],
// ========== 自动更新配置 ==========
"electronUpdater": {
"autoDownload": true, // 自动下载更新
"autoInstallOnAppQuit": true, // 退出时自动安装
"allowDowngrade": false, // 允许降级
"allowPrerelease": false // 允许预发布版本
},
// ========== 其他配置 ==========
"protocols": [ // URL Scheme 配置
{
"name": "my-app-protocol",
"schemes": ["myapp"], // myapp://
"role": "Editor" // Editor/Viewer/Shell/None
}
],
"fileAssociations": [ // 文件关联
{
"ext": "myext", // 文件扩展名
"name": "My File Type", // 文件类型名称
"description": "My File Description", // 文件描述
"icon": "build/fileIcon.icns", // 文件图标
"role": "Editor" // 角色
}
],
"artifactName": "${productName}-${version}-${os}-${arch}.${ext}", // 输出文件名格式
"detectUpdateChannel": true, // 自动检测更新通道
"forceCodeSigning": false, // 强制代码签名
"electronLanguages": [ // Electron 语言包
"zh-CN",
"en-US"
],
"extends": null // 继承配置文件
}
}
配置说明:
- 注释以
//开头的行在实际使用时需要删除(JSON 不支持注释) - 使用时可以只保留需要的字段,其他使用默认值
${变量名}可引用其他字段值或环境变量- 密码等敏感信息建议使用环境变量,如
process.env.CSC_KEY_PASSWORD
五、运行与调试
5.1 启动应用
npm start
5.2 开启调试
方式1:代码中打开
// main.js
mainWindow.webContents.openDevTools();
方式2:快捷键
- macOS:
Cmd + Option + I - Windows/Linux:
Ctrl + Shift + I
方式3:右键菜单
- 右键页面 → 检查元素
5.3 主进程调试
创建 .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "调试主进程",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
},
"args": ["."],
"outputCapture": "std",
"console": "integratedTerminal"
}
]
}
六、打包应用
6.1 准备图标
| 平台 | 格式 | 尺寸 | 路径 |
|---|---|---|---|
| macOS | .icns | 512x512 | build/icon.icns |
| Windows | .ico | 256x256 | build/icon.ico |
| Linux | .png | 512x512 | build/icon.png |
图标生成工具:
- 在线:www.icoconverter.com/
- 命令行:
npm install -g electron-icon-maker
6.2 执行打包
# 当前平台
npm run dist
# 指定平台
npm run dist:mac # macOS
npm run dist:win # Windows
npm run dist:linux # Linux
6.3 输出目录
dist/
├── mac/
│ └── MyApp.app
├── MyApp-1.0.0.dmg
├── win-unpacked/
├── MyApp Setup 1.0.0.exe
├── linux-unpacked/
└── MyApp-1.0.0.AppImage
七、进程间通信 (IPC)
7.1 渲染进程 → 主进程(单向)
// preload.js
contextBridge.exposeInMainWorld('api', {
send: (channel, data) => ipcRenderer.send(channel, data)
});
// main.js
ipcMain.on('message', (event, data) => {
console.log(data);
});
// renderer.js
window.api.send('message', '你好');
7.2 主进程 → 渲染进程
// main.js
mainWindow.webContents.send('reply', '你好,渲染进程');
// preload.js
contextBridge.exposeInMainWorld('api', {
onReply: (callback) => ipcRenderer.on('reply', callback)
});
// renderer.js
window.api.onReply((event, data) => {
console.log(data);
});
7.3 双向通信(invoke/handle)
// main.js
ipcMain.handle('get-data', async (event, arg) => {
return await fetchData(arg);
});
// preload.js
contextBridge.exposeInMainWorld('api', {
getData: (arg) => ipcRenderer.invoke('get-data', arg)
});
// renderer.js
const data = await window.api.getData('参数');
八、常用功能
8.1 系统托盘
const { app, Tray, Menu } = require('electron');
const path = require('path');
let tray = null;
app.whenReady().then(() => {
tray = new Tray(path.join(__dirname, 'tray-icon.png'));
const contextMenu = Menu.buildFromTemplate([
{ label: '显示', click: () => mainWindow.show() },
{ label: '退出', click: () => app.quit() }
]);
tray.setContextMenu(contextMenu);
tray.setToolTip('我的应用');
});
8.2 系统通知
const { Notification } = require('electron');
new Notification({
title: '标题',
body: '内容',
icon: path.join(__dirname, 'icon.png')
}).show();
8.3 文件对话框
const { dialog } = require('electron');
// 打开文件
const result = await dialog.showOpenDialog({
properties: ['openFile', 'multiSelections'],
filters: [
{ name: 'Images', extensions: ['jpg', 'png'] },
{ name: 'All Files', extensions: ['*'] }
]
});
// 保存文件
const saveResult = await dialog.showSaveDialog({
defaultPath: 'untitled.txt'
});
8.4 自定义菜单
const { Menu } = require('electron');
const template = [
{
label: '文件',
submenu: [
{ label: '新建', accelerator: 'CmdOrCtrl+N', click: () => {} },
{ type: 'separator' },
{ role: 'quit', label: '退出' }
]
},
{
label: '编辑',
submenu: [
{ role: 'undo', label: '撤销' },
{ role: 'redo', label: '重做' },
{ type: 'separator' },
{ role: 'cut', label: '剪切' },
{ role: 'copy', label: '复制' },
{ role: 'paste', label: '粘贴' }
]
}
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
8.5 全局快捷键
const { globalShortcut } = require('electron');
app.whenReady().then(() => {
globalShortcut.register('CommandOrControl+X', () => {
console.log('快捷键触发');
});
});
app.on('will-quit', () => {
globalShortcut.unregisterAll();
});
九、自动更新
9.1 安装依赖
npm install electron-updater
9.2 主进程配置
const { autoUpdater } = require('electron-updater');
app.whenReady().then(() => {
// 检查更新
autoUpdater.checkForUpdatesAndNotify();
});
// 监听更新事件
autoUpdater.on('update-available', () => {
console.log('有新版本');
});
autoUpdater.on('update-downloaded', () => {
console.log('下载完成');
// 退出并安装
autoUpdater.quitAndInstall();
});
9.3 发布配置
在 package.json 的 build 中配置:
{
"build": {
"publish": [
{
"provider": "github",
"owner": "your-username",
"repo": "your-repo"
}
]
}
}
十、安全最佳实践
10.1 安全检查清单
// main.js
const mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: false, // ✅ 必须:禁用 Node 集成
contextIsolation: true, // ✅ 必须:启用上下文隔离
enableRemoteModule: false, // ✅ 必须:禁用 remote 模块
sandbox: true, // ✅ 推荐:启用沙箱
webSecurity: true, // ✅ 必须:启用 Web 安全
allowRunningInsecureContent: false, // ✅ 必须:禁止不安全内容
preload: path.join(__dirname, 'preload.js')
}
});
10.2 CSP 配置
<!-- index.html -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';">
10.3 验证 IPC 消息
// preload.js
contextBridge.exposeInMainWorld('api', {
sendMessage: (channel, data) => {
// 白名单验证
const validChannels = ['allowed-channel-1', 'allowed-channel-2'];
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
}
});
十一、常见问题
11.1 启动白屏
原因: 路径错误或资源加载失败
解决:
// 使用绝对路径
mainWindow.loadFile(path.join(__dirname, 'index.html'));
11.2 打包后无法运行
检查:
package.json的main字段是否正确files配置是否包含所有必要文件- 查看控制台错误信息
11.3 macOS "已损坏"提示
临时解决(仅测试):
sudo xattr -r -d com.apple.quarantine /path/to/YourApp.app
正确做法: 代码签名和公证
十二、项目结构建议
小型项目
my-electron-app/
├── main.js
├── preload.js
├── index.html
├── style.css
├── renderer.js
├── package.json
└── build/
├── icon.icns
├── icon.ico
└── icon.png
中大型项目
my-electron-app/
├── src/
│ ├── main/ # 主进程
│ │ ├── index.js
│ │ ├── menu.js
│ │ ├── tray.js
│ │ └── ipc.js
│ ├── renderer/ # 渲染进程
│ │ ├── index.html
│ │ ├── index.js
│ │ └── styles/
│ └── preload/
│ └── index.js
├── assets/ # 资源文件
├── build/ # 构建配置
├── dist/ # 打包输出
└── package.json
十三、学习资源
- 官方文档:www.electronjs.org/docs/latest
- 中文文档:www.electronjs.org/zh/docs/lat…
- API 参考:www.electronjs.org/docs/latest…
- Electron Fiddle:www.electronjs.org/fiddle
十四、快速命令
# 初始化
npm init -y
npm install electron electron-builder --save-dev
# 运行
npm start
# 打包
npm run dist
npm run dist:mac
npm run dist:win
npm run dist:linux
# 清理
rm -rf node_modules dist
npm install