Electron 使用教程:从入门到实践

📅 更新时间:2026年5月14日 🎯 适用版本:Electron 28+ 💡 适合人群:桌面应用开发者、Web前端转桌面开发者


1. Electron 简介

1.1 什么是 Electron?

Electron 是一个使用 Web 技术(HTML、CSS、JavaScript)构建跨平台桌面应用的框架。由 GitHub 开发维护,著名的 VS Code、Slack、Notion 等应用都基于 Electron 构建。

1.2 核心优势

优势说明
跨平台一次开发,Windows/macOS/Linux 均可运行
技术栈统一前端开发者可快速上手
生态丰富可直接使用 npm 生态中的百万级包
开发效率高支持热重载,快速迭代
原生能力可访问文件系统、系统托盘、通知等

1.3 架构概览

┌─────────────────────────────────────────────────┐
│                   Electron                       │
│  ┌───────────────┐         ┌───────────────┐   │
│  │   Main Process │◄───────►│ Render Process │   │
│  │   (主进程)      │  IPC    │   (渲染进程)   │   │
│  └───────────────┘         └───────────────┘   │
│         │                           │          │
│         ▼                           ▼          │
│  ┌───────────────┐         ┌───────────────┐    │
│  │   Node.js     │         │   Chromium   │    │
│  │   Runtime     │         │   Browser    │    │
│  └───────────────┘         └───────────────┘    │
└─────────────────────────────────────────────────┘

2. 核心概念

2.1 主进程 (Main Process)

  • 唯一性:每个应用只有一个主进程
  • 职责:创建窗口、管理应用生命周期、调用原生 API
  • 运行环境:Node.js 环境

2.2 渲染进程 (Renderer Process)

  • 多实例:每个窗口对应一个渲染进程
  • 职责:负责 UI 渲染、用户交互
  • 运行环境:Chromium + 限制版的 Node.js

2.3 预加载脚本 (Preload Script)

  • 桥接主进程和渲染进程的安全通道
  • 通过 contextBridge 暴露安全的 API 给渲染进程

2.4 进程隔离

⚠️ 安全模型:
- 渲染进程默认无法访问 Node.js API
- 渲染进程无法直接访问原生资源
- 通过 preload 脚本和 contextBridge 实现安全通信

3. 快速开始

3.1 环境准备

# Node.js 版本要求 (Electron 28+)
node >= 18.0.0
npm >= 9.0.0
​
# 验证版本
node -v
npm -v

3.2 项目初始化

方式一:使用脚手架(推荐)

# 创建项目
npm create electron-app@latest my-electron-app
​
# 进入目录
cd my-electron-app
​
# 启动开发模式
npm run dev

方式二:手动创建

# 创建项目目录
mkdir my-electron-app && cd my-electron-app
​
# 初始化 package.json
npm init -y
​
# 安装 Electron (开发依赖)
npm install electron --save-dev
​
# 安装 electron-builder (打包工具)
npm install electron-builder --save-dev

3.3 目录结构

my-electron-app/
├── package.json
├── main.js              # 主进程入口
├── preload.js           # 预加载脚本
├── src/
│   ├── index.html       # 渲染进程 HTML
│   ├── index.css        # 样式文件
│   └── renderer.js      # 渲染进程 JS
└── build/               # 打包资源

3.4 最小化示例

main.js (主进程)

const { app, BrowserWindow } = require('electron');
const path = require('path');
​
function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    title: 'My Electron App',
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,      // 启用上下文隔离
      nodeIntegration: false,       // 禁用 Node.js 集成
      sandbox: true                // 启用沙箱
    }
  });
​
  // 加载页面
  mainWindow.loadFile(path.join(__dirname, 'src', 'index.html'));
​
  // 打开开发者工具 (开发模式)
  if (process.env.NODE_ENV === 'development') {
    mainWindow.webContents.openDevTools();
  }
}
​
// 应用就绪后创建窗口
app.whenReady().then(createWindow);
​
// 所有窗口关闭时退出应用 (macOS 除外)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});
​
// macOS 点击 Dock 图标时重新创建窗口
app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

preload.js (预加载脚本)

const { contextBridge, ipcRenderer } = require('electron');
​
// 暴露安全的 API 给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
  // 窗口控制
  minimizeWindow: () => ipcRenderer.send('window-minimize'),
  maximizeWindow: () => ipcRenderer.send('window-maximize'),
  closeWindow: () => ipcRenderer.send('window-close'),
​
  // 读取文件
  readFile: (filePath) => ipcRenderer.invoke('read-file', filePath),
​
  // 监听事件
  onUpdate: (callback) => ipcRenderer.on('update-data', callback)
});

index.html (渲染进程)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My Electron App</title>
  <link rel="stylesheet" href="index.css">
</head>
<body>
  <h1>Hello Electron!</h1>
  <button id="minimizeBtn">最小化</button>
  <button id="maximizeBtn">最大化</button>
  <button id="closeBtn">关闭</button>

  <script src="renderer.js"></script>
</body>
</html>

renderer.js (渲染进程)

// 通过 preload 暴露的 API 与主进程通信
document.getElementById('minimizeBtn').addEventListener('click', () => {
  window.electronAPI.minimizeWindow();
});

document.getElementById('maximizeBtn').addEventListener('click', () => {
  window.electronAPI.maximizeWindow();
});

document.getElementById('closeBtn').addEventListener('click', () => {
  window.electronAPI.closeWindow();
});

3.5 配置 package.json

{
  "name": "my-electron-app",
  "version": "1.0.0",
  "description": "My Electron Application",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "dev": "NODE_ENV=development electron .",
    "build": "electron-builder",
    "build:win": "electron-builder --win",
    "build:mac": "electron-builder --mac",
    "build:linux": "electron-builder --linux"
  },
  "build": {
    "appId": "com.myapp.electron",
    "productName": "MyElectronApp",
    "directories": {
      "output": "dist"
    },
    "win": {
      "target": ["nsis"],
      "icon": "build/icon.ico"
    },
    "mac": {
      "target": ["dmg"],
      "icon": "build/icon.icns"
    },
    "linux": {
      "target": ["AppImage"],
      "icon": "build/icon.png"
    }
  }
}

4. 主进程与渲染进程

4.1 生命周期

应用启动 → ready → window-all-closed → quit
                ↓
           window 创建
                ↓
           窗口关闭
                ↓
           activate (macOS)

4.2 app 模块常用 API

const { app } = require('electron');

// 获取应用信息
console.log(app.getPath('userData'));      // 用户数据目录
console.log(app.getPath('downloads'));     // 下载目录
console.log(app.getName());                  // 应用名称
console.log(app.getVersion());              // 应用版本

// 应用事件
app.on('ready', () => { /* 应用就绪 */ });
app.on('window-all-closed', () => { /* 所有窗口关闭 */ });
app.on('activate', () => { /* macOS 激活 */ });
app.on('before-quit', () => { /* 退出前 */ });

// 控制应用
app.quit();              // 退出应用
app.exit();              // 强制退出
app.relaunch();          // 重启应用

4.3 BrowserWindow 常用配置

const mainWindow = new BrowserWindow({
  // 尺寸
  width: 1200,
  height: 800,
  minWidth: 800,
  minHeight: 600,

  // 位置
  x: 100,
  y: 100,
  center: true,              // 居中显示

  // 外观
  title: 'My App',
  backgroundColor: '#ffffff',
  opacity: 1,

  // 窗口行为
  resizable: true,           // 可调整大小
  movable: true,             // 可移动
  minimizable: true,         // 可最小化
  maximizable: true,         // 可最大化
  closable: true,             // 可关闭

  // 帧
  frame: true,               // 显示窗口边框 (false = 无边框窗口)
  titleBarStyle: 'hidden',   // macOS 隐藏标题栏

  // Web 偏好
  webPreferences: {
    preload: path.join(__dirname, 'preload.js'),
    contextIsolation: true,
    nodeIntegration: false,
    sandbox: true
  }
});

5. 进程间通信 (IPC)

5.1 IPC 通信模式

┌─────────────┐    invoke    ┌─────────────┐
│   Renderer  │ ──────────► │    Main     │
│             │   sendSync   │             │
│             │ ◄────────── │             │
│             │    reply     │             │
└─────────────┘              └─────────────┘

┌─────────────┐    send      ┌─────────────┐
│   Renderer  │ ──────────► │    Main     │
│             │   on/listen │             │
└─────────────┘              └─────────────┘

5.2 主进程 → 渲染进程

主进程 (main.js)

const { ipcMain, BrowserWindow } = require('electron');

// 发送消息到指定窗口
mainWindow.webContents.send('update-data', { message: 'Hello from main!' });

// 回复渲染进程的请求
ipcMain.handle('get-app-info', async (event, args) => {
  return {
    version: app.getVersion(),
    platform: process.platform
  };
});

渲染进程 (preload.js)

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  // 双向通信 - 发送请求并等待回复
  getAppInfo: () => ipcRenderer.invoke('get-app-info'),

  // 监听主进程消息
  onUpdate: (callback) => {
    ipcRenderer.on('update-data', (event, data) => callback(data));
  },

  // 移除监听器
  removeUpdateListener: () => {
    ipcRenderer.removeAllListeners('update-data');
  }
});

5.3 渲染进程 → 主进程

渲染进程 (renderer.js)

// 调用主进程的方法
const info = await window.electronAPI.getAppInfo();
console.log(info.version, info.platform);

// 发送窗口控制命令
window.electronAPI.minimizeWindow();

主进程 (main.js)

// 处理窗口控制
ipcMain.on('window-minimize', (event) => {
  const win = BrowserWindow.fromWebContents(event.sender);
  win.minimize();
});

ipcMain.on('window-maximize', (event) => {
  const win = BrowserWindow.fromWebContents(event.sender);
  if (win.isMaximized()) {
    win.unmaximize();
  } else {
    win.maximize();
  }
});

ipcMain.on('window-close', (event) => {
  const win = BrowserWindow.fromWebContents(event.sender);
  win.close();
});

6. 窗口管理

6.1 多窗口管理

const { BrowserWindow } = require('electron');

let mainWindow;
let settingsWindow;

// 创建主窗口
function createMainWindow() {
  mainWindow = new BrowserWindow({ width: 1200, height: 800 });
  mainWindow.loadFile('index.html');

  mainWindow.on('closed', () => {
    mainWindow = null;
  });
}

// 创建子窗口
function createSettingsWindow() {
  if (settingsWindow) {
    settingsWindow.focus();
    return;
  }

  settingsWindow = new BrowserWindow({
    width: 600,
    height: 400,
    parent: mainWindow,      // 模态窗口,阻塞父窗口
    modal: true,
    resizable: false
  });

  settingsWindow.loadFile('settings.html');

  settingsWindow.on('closed', () => {
    settingsWindow = null;
  });
}

6.2 无边框窗口

const mainWindow = new BrowserWindow({
  frame: false,           // 隐藏原生窗口边框
  titleBarStyle: 'hidden', // macOS 隐藏标题栏但保留控制按钮

  // 交通灯按钮位置 (macOS)
  titleBarOverlay: {
    color: '#2d2d2d',
    symbolColor: '#ffffff',
    height: 50
  }
});

// 自定义拖拽区域
// 在 HTML 中添加 style="webkit-app-region: drag;"
// 可拖动元素添加 style="webkit-app-region: no-drag;"

6.3 窗口状态管理

// 保存窗口状态
const fs = require('fs');

function saveWindowState() {
  const bounds = mainWindow.getBounds();
  const isMaximized = mainWindow.isMaximized();

  fs.writeFileSync('window-state.json', JSON.stringify({
    bounds,
    isMaximized
  }));
}

// 恢复窗口状态
function restoreWindowState() {
  let state = { width: 1200, height: 800, x: undefined, y: undefined };

  try {
    state = JSON.parse(fs.readFileSync('window-state.json'));
  } catch (e) {}

  return state;
}

7. 系统集成

7.1 系统托盘

const { Tray, Menu, nativeImage } = require('electron');
const path = require('path');

let tray;

function createTray() {
  // 创建托盘图标
  const icon = nativeImage.createFromPath(
    path.join(__dirname, 'assets', 'tray-icon.png')
  );

  tray = new Tray(icon);

  const contextMenu = Menu.buildFromTemplate([
    { label: '打开主窗口', click: () => mainWindow.show() },
    { label: '设置', click: () => createSettingsWindow() },
    { type: 'separator' },
    { label: '关于', click: () => showAboutDialog() },
    { type: 'separator' },
    { label: '退出', click: () => app.quit() }
  ]);

  tray.setToolTip('My Electron App');
  tray.setContextMenu(contextMenu);

  // 单击托盘图标显示窗口
  tray.on('click', () => {
    mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show();
  });
}

7.2 系统通知

const { Notification } = require('electron');

function showNotification(title, body) {
  if (Notification.isSupported()) {
    const notification = new Notification({
      title,
      body,
      icon: path.join(__dirname, 'assets', 'icon.png'),
      silent: false
    });

    notification.on('click', () => {
      mainWindow.show();
      mainWindow.focus();
    });

    notification.show();
  }
}

7.3 菜单栏

const { Menu } = require('electron');

function createMenu() {
  const template = [
    {
      label: '文件',
      submenu: [
        { label: '新建', accelerator: 'CmdOrCtrl+N', click: () => {} },
        { label: '打开', accelerator: 'CmdOrCtrl+O', click: () => openFile() },
        { type: 'separator' },
        { label: '退出', accelerator: 'CmdOrCtrl+Q', click: () => app.quit() }
      ]
    },
    {
      label: '编辑',
      submenu: [
        { role: 'undo' },
        { role: 'redo' },
        { type: 'separator' },
        { role: 'cut' },
        { role: 'copy' },
        { role: 'paste' },
        { role: 'selectAll' }
      ]
    },
    {
      label: '视图',
      submenu: [
        { role: 'reload' },
        { role: 'forceReload' },
        { role: 'toggleDevTools' },
        { type: 'separator' },
        { role: 'resetZoom' },
        { role: 'zoomIn' },
        { role: 'zoomOut' },
        { type: 'separator' },
        { role: 'togglefullscreen' }
      ]
    },
    {
      label: '帮助',
      submenu: [
        { label: '关于', click: () => showAboutDialog() }
      ]
    }
  ];

  const menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
}

7.4 拖放文件

// 主进程处理文件拖放
mainWindow.webContents.on('will-navigate', (event) => {
  event.preventDefault();
});

// 渲染进程处理拖放
const holder = document.getElementById('drop-zone');

holder.ondragover = (e) => {
  e.preventDefault();
  e.stopPropagation();
};

holder.ondrop = (e) => {
  e.preventDefault();
  e.stopPropagation();

  const files = [...e.dataTransfer.files];
  files.forEach(file => {
    console.log('Dropped file:', file.path);
  });
};

8. 打包与发布

8.1 electron-builder 配置

{
  "appId": "com.gamebox.app",
  "productName": "GameBox",
  "copyright": "Copyright © 2026",

  "directories": {
    "output": "release",
    "buildResources": "build"
  },

  "files": [
    "main.js",
    "preload.js",
    "src/**/*",
    "assets/**/*"
  ],

  "win": {
    "target": [
      {
        "target": "nsis",
        "arch": ["x64"]
      },
      {
        "target": "portable",
        "arch": ["x64"]
      }
    ],
    "icon": "build/icon.ico",
    "artifactName": "${productName}-${version}-${arch}.${ext}"
  },

  "mac": {
    "target": ["dmg", "zip"],
    "icon": "build/icon.icns",
    "category": "public.app-category.games"
  },

  "linux": {
    "target": ["AppImage", "deb"],
    "icon": "build/icon.png",
    "category": "Game"
  },

  "nsis": {
    "oneClick": false,
    "allowToChangeInstallationDirectory": true,
    "createDesktopShortcut": true,
    "createStartMenuShortcut": true
  }
}

8.2 打包命令

# 开发环境启动
npm run dev

# 构建 Windows 安装包
npm run build:win

# 构建 macOS 应用
npm run build:mac

# 构建 Linux 应用
npm run build:linux

# 构建所有平台
npm run build

8.3 自动更新

const { autoUpdater } = require('electron-updater');

function setupAutoUpdater() {
  autoUpdater.autoDownload = false;

  autoUpdater.on('update-available', (info) => {
    mainWindow.webContents.send('update-available', info);
  });

  autoUpdater.on('update-downloaded', (info) => {
    mainWindow.webContents.send('update-downloaded', info);
  });

  autoUpdater.on('error', (err) => {
    console.error('Update error:', err);
  });

  // 检查更新
  autoUpdater.checkForUpdates();
}

// 安装更新并重启
function installUpdate() {
  autoUpdater.quitAndInstall();
}

9. 常见问题与最佳实践

9.1 安全最佳实践

// ✅ 始终启用安全配置
webPreferences: {
  contextIsolation: true,      // 隔离上下文
  nodeIntegration: false,     // 禁用 Node 集成
  sandbox: true,              // 启用沙箱
  webSecurity: true,          // 启用 Web 安全
  allowRunningInsecureContent: false
}

// ❌ 避免的安全问题
webPreferences: {
  nodeIntegration: true,       // 危险!
  contextIsolation: false,     // 危险!
  enableRemoteModule: true     // 已废弃,危险!
}

9.2 性能优化

// 1. 延迟加载窗口
app.whenReady().then(() => {
  setTimeout(createWindow, 1000);  // 延迟 1 秒
});

// 2. 使用内容安全策略
// 在 index.html 的 <head> 中添加
<meta http-equiv="Content-Security-Policy" 
      content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">

// 3. 禁用硬件加速(如果出现渲染问题)
app.disableHardwareAcceleration();

// 4. 使用 webFrame API 优化
const { webFrame } = require('electron');
webFrame.setZoomFactor(1);
webFrame.setVisualZoomLevelLimits(1, 1);

9.3 调试技巧

// 开发模式打开 DevTools
if (process.env.NODE_ENV === 'development') {
  mainWindow.webContents.openDevTools();
}

// 主进程调试
console.log('Debug info:', process.argv);

// 渲染进程调试
window.electronAPI.debug && window.electronAPI.debug();

9.4 常见错误处理

// 捕获未处理的异常
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
});

// 捕获未处理的 Promise 拒绝
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection:', reason);
});

// 窗口加载失败处理
mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDesc) => {
  console.error('Failed to load:', errorCode, errorDesc);
});

// 网络错误处理
mainWindow.webContents.on('did-fail-provisional-load', (event, errorCode) => {
  if (errorCode === -10) { // ERR_BLOCKED_BY_CLIENT
    console.log('Resource blocked');
  }
});

附录:常用 npm 包

包名用途
electronElectron 核心
electron-builder应用打包
electron-updater自动更新
electron-log日志记录
electron-store数据持久化
electron-squirrel-startupSquirrel 安装支持

参考资源


💡 提示:本文档会持续更新。如有问题或建议,欢迎在评论区留言!

📧 联系作者:[CSDN 博客地址]

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容