Electron 教程

Electron 教程

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

本教程包含

  • 基础篇:简介、架构、安装、入门实例、开发工具、核心概念
  • 进阶篇:窗口管理、菜单、对话框、系统托盘、IPC 通信、渲染进程
  • 应用篇:文件系统、网络请求、数据库、调试、打包发布、安全性

为什么选择 Electron

  • 一套代码,Windows/macOS/Linux 三平台
  • 使用熟悉的 Web 技术栈
  • 生态成熟:VS Code、Slack 等证明可行性
  • 前端开发转型桌面应用的最快路径

局限

打包体积大(内含 Chromium)
内存占用相对较高
性能敏感场景需谨慎

Electron 简介

Electron 简介

Electron 由 GitHub 于 2013 年创建(原名 Atom Shell),2015 年改名 Electron,现已由 OpenJS 基金会维护。

核心技术

Chromium: 渲染界面(Web 技术)
Node.js:  主进程能力(文件/网络/系统)
V8 引擎:  JavaScript 运行时

Electron = Chromium + Node.js + 原生 API

知名应用

VS Code / Slack / Discord
Notion / Figma(桌面版) / WhatsApp(桌面版)
飞书 / 钉钉(桌面版) / Postman

进程模型

主进程(Main Process):
  应用入口,管理生命周期与原生能力
  每个应用只有一个

渲染进程(Renderer Process):
  每个窗口一个,运行 Web 页面
  可多个,负责 UI

发展现状

Electron 5.x 至今已发布 30+ 大版本,配合现代 Web 技术(React/Vue)开发体验很好。虽有 Tauri 等新竞争者,Electron 生态仍最成熟。

Electron 架构

Electron 架构

理解 Electron 的主进程/渲染进程架构,是开发的基础。

进程架构图

┌─────────────────────────────┐
│       主进程 (Node.js)        │
│  - 应用生命周期               │
│  - 创建窗口                   │
│  - 原生能力(菜单/托盘/对话框)  │
│  - 系统 API 访问              │
└──────────────┬──────────────┘
               │ IPC 通信
   ┌───────────┴───────────┐
   │      渲染进程 (Chromium) │
   │  - HTML/CSS/JS UI      │
   │  - 浏览器 API           │
   │  - 无 Node 权限(默认)    │
   └───────────────────────┘

两种进程的职责

主进程:
  app 模块: 生命周期
  BrowserWindow: 创建窗口
  Menu/Tray/dialog: 原生 UI
  Node.js 全部能力(文件/网络)

渲染进程:
  DOM/Web API
  页面逻辑与交互
  (可用 preload 安全地暴露主进程能力)

IPC(进程间通信)

主进程 ⇄ 渲染进程 通过 ipcMain / ipcRenderer
安全实践: 渲染进程不直接访问 Node,经 preload 暴露

开发模式

主进程: main.js(CommonJS)
渲染进程: 常规 Web 开发
preload: 桥接层(contextBridge)

Electron 安装

Electron 安装

安装 Electron 并创建第一个项目骨架。

环境要求

Node.js 18+ (推荐 LTS)
npm 或 yarn 或 pnpm
(Windows 构建可选 Visual Studio Build Tools)

方式一: 手动初始化

mkdir my-app && cd my-app
npm init -y
npm install --save-dev electron

# 创建 main.js
# 修改 package.json: "main": "main.js"
# 添加启动脚本: "start": "electron ."

方式二: 脚手架(推荐)

# electron-forge(官方推荐)
npx create-electron-app my-app
cd my-app
npm start

# 或 Vite 模板
npm create @quick-start/electron

镜像加速(国内)

# .npmrc
electron_mirror=https://npmmirror.com/mirrors/electron/

验证安装

npx electron --version
# v30.x.x

最小 main.js

const { app, BrowserWindow } = require("electron");

function createWindow() {
  const win = new BrowserWindow({ width: 800, height: 600 });
  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();
});

Electron 入门实例

Electron 入门实例

从零构建一个可运行的 Hello World 桌面应用。

项目结构

my-app/
├── package.json
├── main.js          # 主进程
├── preload.js       # 预加载脚本
└── index.html       # 渲染页面

package.json

{
  "name": "my-app",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "devDependencies": {
    "electron": "^30.0.0"
  }
}

main.js(主进程)

const { app, BrowserWindow } = require("electron");
const path = require("path");

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
    },
  });
  win.loadFile("index.html");
}

app.whenReady().then(createWindow);

app.on("window-all-closed", () => {
  if (process.platform !== "darwin") app.quit();
});

preload.js

const { contextBridge } = require("electron");

// 安全暴露 API 给渲染进程
contextBridge.exposeInMainWorld("versions", {
  node: process.versions.node,
  chrome: process.versions.chrome,
  electron: process.versions.electron,
});

index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello Electron</title>
</head>
<body>
  <h1>Hello Electron!</h1>
  <p id="info"></p>
  <script>
    document.getElementById("info").textContent =
      `Node ${window.versions.node}, Electron ${window.versions.electron}`;
  </script>
</body>
</html>

运行

npm start   # 启动应用

Electron 开发工具

Electron 开发工具

高效开发 Electron 应用的工具链配置。

DevTools(内置)

// 主进程打开开发者工具
win.webContents.openDevTools();

// 或菜单: View → Toggle Developer Tools
// 快捷键: Cmd/Ctrl+Shift+I

热重载

// 渲染进程热更新(配合 Vite/Webpack)
// 主进程改动需重启: nodemon electron .

npm install -D nodemon
// package.json
"start": "nodemon --exec electron ."

VS Code 调试

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [{
    "type": "node",
    "request": "launch",
    "name": "Debug Electron",
    "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
    "args": ["."]
  }]
}

Electron Fiddle(官方实验工具)

快速创建/测试 Electron 片段
下载: electronjs.org/fiddle

DevTools 扩展

// 加载 React/Vue DevTools 等浏览器扩展
win.webContents.session.loadExtension("/path/to/extension")

调试渲染进程

win.webContents.on("did-finish-load", () => {
  win.webContents.openDevTools({ mode: "detach" });
});

// 监听渲染进程崩溃
win.webContents.on("render-process-gone", (event, details) => {
  console.error("渲染进程崩溃:", details.reason);
});

Electron 核心概念

Electron 核心概念

掌握 Electron 开发必备的核心模块与概念。

核心模块

app            应用生命周期
BrowserWindow  窗口
Menu           应用菜单
Tray           系统托盘
dialog         原生对话框
ipcMain/ipcRenderer  进程通信
shell          外部程序/文件操作
clipboard      剪贴板
screen         屏幕信息
globalShortcut 全局快捷键

app 生命周期事件

app.on("ready", ...)        // 初始化完成(推荐 whenReady)
app.on("window-all-closed") // 所有窗口关闭
app.on("before-quit")       // 退出前
app.on("activate")          // macOS 点击 Dock 图标

// 常用方法
app.quit()          // 退出应用
app.getVersion()    // 应用版本
app.getPath("userData")  // 用户数据目录

BrowserWindow 常用配置

new BrowserWindow({
  width: 1024, height: 768,
  title: "应用标题",
  icon: "icon.png",
  resizable: true,
  minimizable: true,
  maximizable: true,
  frame: true,          // false = 无边框窗口
  transparent: false,   // 透明窗口
  alwaysOnTop: false,   // 置顶
  webPreferences: {
    preload: "preload.js",
    contextIsolation: true,   // 上下文隔离(安全)
    nodeIntegration: false,   // 渲染进程禁用 Node(安全)
  },
})

安全最佳实践

1. contextIsolation: true(默认)
2. nodeIntegration: false(默认)
3. 用 preload + contextBridge 暴露 API
4. 验证渲染进程输入(防 XSS)
5. 设置内容安全策略(CSP)

Electron 创建和管理窗口

Electron 创建和管理窗口

窗口是应用的基本容器,掌握创建、配置与多窗口管理。

创建窗口

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

const win = new BrowserWindow({
  width: 900,
  height: 600,
  minWidth: 400,
  minHeight: 300,
});

// 加载内容
win.loadURL("https://example.com");   // 远程页面
win.loadFile("index.html");           // 本地文件

// 创建后
win.show();                 // 显示
win.focus();                // 聚焦
win.setTitle("新标题");      // 改标题
win.setSize(1200, 800);     // 改尺寸
win.maximize();             // 最大化
win.minimize();             // 最小化
win.close();                // 关闭

多窗口管理

function createWindow(url) {
  const win = new BrowserWindow({ width: 800, height: 600 });
  win.loadURL(url);
  return win;
}

const win1 = createWindow("https://a.com");
const win2 = createWindow("https://b.com");

// 获取所有窗口
BrowserWindow.getAllWindows().forEach(w => w.show());

窗口事件

win.on("closed", () => console.log("窗口已关闭"));
win.on("focus", () => console.log("获得焦点"));
win.on("blur", () => console.log("失去焦点"));
win.on("resize", () => console.log("尺寸变化"));
win.on("maximize", () => console.log("最大化"));

// 关闭前拦截(确认对话框)
win.on("close", (e) => {
  const choice = require("electron").dialog.showMessageBoxSync(win, {
    type: "question",
    buttons: ["确定", "取消"],
    message: "确定退出吗?",
  });
  if (choice !== 0) e.preventDefault();   // 阻止关闭
});

无边框窗口

const win = new BrowserWindow({ frame: false });
// 需要自定义标题栏与拖拽区域
// CSS: -webkit-app-region: drag

Electron 菜单

Electron 菜单

创建应用菜单栏、上下文菜单与快捷键。

应用菜单

const { Menu, app } = require("electron");

const template = [
  { label: "文件", submenu: [
      { label: "新建", accelerator: "CmdOrCtrl+N",
        click: () => createNewFile() },
      { type: "separator" },
      { label: "退出", role: "quit" },
  ]},
  { label: "编辑", submenu: [
      { role: "undo" }, { role: "redo" },
      { type: "separator" },
      { role: "cut" }, { role: "copy" }, { role: "paste" },
  ]},
  { label: "视图", submenu: [
      { role: "reload" }, { role: "toggleDevTools" },
      { type: "separator" },
      { role: "togglefullscreen" },
  ]},
  { label: "帮助", role: "help", submenu: [
      { label: "关于", click: () => showAbout() },
  ]},
];

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

上下文菜单(右键)

win.webContents.on("context-menu", (event, params) => {
  const menu = Menu.buildFromTemplate([
    { label: "复制", role: "copy" },
    { label: "粘贴", role: "paste" },
    { type: "separator" },
    { label: "刷新", role: "reload" },
  ]);
  menu.popup({ window: win });
});

role 预置角色

role: "quit" / "reload" / "copy" / "paste" / "cut"
role: "undo" / "redo" / "toggleDevTools" / "togglefullscreen"
role: "minimize" / "close" / "about"

快捷键(accelerator)

CmdOrCtrl+N   // Cmd(mac) / Ctrl(win)
CmdOrCtrl+Shift+P
Alt+F4
accelerator 与菜单项 click 配合实现全局快捷键

Electron 对话框

Electron 对话框

dialog 模块提供原生文件选择、消息提示、保存对话框。

消息对话框

const { dialog, app } = require("electron");

// 同步(阻塞)
const result = dialog.showMessageBoxSync({
  type: "question",           // info/warning/error/question
  title: "确认",
  message: "确定要删除该文件吗?",
  detail: "此操作不可撤销",
  buttons: ["删除", "取消"],
  defaultId: 1,               // 默认焦点按钮
  cancelId: 1,                // ESC 对应按钮
});
if (result === 0) deleteFile();

// 异步(推荐)
dialog.showMessageBox(win, {...}).then(({ response }) => {...});

打开文件对话框

const files = dialog.showOpenDialogSync({
  title: "选择文件",
  properties: ["openFile", "multiSelections"],  // 可多选
  filters: [
    { name: "图片", extensions: ["png", "jpg", "gif"] },
    { name: "所有文件", extensions: ["*"] },
  ],
});
// files: 选择的文件路径数组 或 undefined

选择目录

const dir = dialog.showOpenDialogSync({
  properties: ["openDirectory"],
});

保存对话框

const file = dialog.showSaveDialogSync({
  title: "保存文件",
  defaultPath: "untitled.txt",
  filters: [{ name: "文本", extensions: ["txt"] }],
});
// file: 保存路径

错误提示

dialog.showErrorBox("错误", "文件读取失败!");
// 或 showMessageBox type: "error"

注意

showXxxDialogSync 会阻塞主进程,推荐用异步版
对话框必须在 app ready 之后调用

Electron 系统托盘

Electron 系统托盘

Tray 让应用常驻系统托盘区(通知栏),提供快捷操作。

创建托盘

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

let tray = null;

app.whenReady().then(() => {
  const icon = nativeImage.createFromPath("tray.png");
  tray = new Tray(icon);

  const contextMenu = Menu.buildFromTemplate([
    { label: "显示主窗口", click: () => showWindow() },
    { label: "最小化到托盘", click: () => hideWindow() },
    { type: "separator" },
    { label: "退出", click: () => app.quit() },
  ]);

  tray.setToolTip("我的应用");
  tray.setContextMenu(contextMenu);

  // 左键点击显示窗口
  tray.on("click", () => showWindow());
});

function showWindow() {
  const win = BrowserWindow.getAllWindows()[0];
  if (win) { win.show(); win.focus(); }
}

关闭到托盘

let isQuitting = false;

win.on("close", (e) => {
  if (!isQuitting) {
    e.preventDefault();       // 阻止退出
    win.hide();               // 隐藏到托盘
    tray.displayBalloon({
      title: "提示",
      content: "应用仍在后台运行",
    });
  }
});

// 托盘"退出"菜单设置 isQuitting = true 再 quit
function quitApp() {
  isQuitting = true;
  app.quit();
}

托盘图标注意

macOS: 建议使用 16x16/32x32 模板图片
Windows: .ico 或多尺寸 png
Linux: 支持 png,注意桌面环境差异

气泡通知

tray.displayBalloon({
  title: "通知标题",
  content: "通知内容",
  icon: "notify.png",    // Windows
});

// 或 Notification(跨平台)
new Notification({ title: "标题", body: "内容" }).show();

Electron IPC 通信

Electron IPC 通信

IPC(进程间通信)是主进程与渲染进程交换数据的核心机制。

方向与模块

渲染 → 主: ipcRenderer.send/invoke + ipcMain.on/handle
主 → 渲染: win.webContents.send + ipcRenderer.on

推荐 invoke/handle(异步,返回 Promise)

渲染进程 → 主进程(invoke)

// preload.js
const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("api", {
  readFile: (path) => ipcRenderer.invoke("read-file", path),
});

// main.js
const { ipcMain } = require("electron");
const fs = require("fs/promises");

ipcMain.handle("read-file", async (event, filePath) => {
  try {
    return { ok: true, data: await fs.readFile(filePath, "utf-8") };
  } catch (err) {
    return { ok: false, error: err.message };
  }
});

// 渲染进程 index.html
const result = await window.api.readFile("data.txt");
console.log(result.data);

单向发送(send/on)

// 渲染 → 主
ipcRenderer.send("log", "用户点击了按钮");

// 主进程
ipcMain.on("log", (event, message) => {
  console.log("来自渲染进程:", message);
});

主进程 → 渲染进程

// 主进程广播
win.webContents.send("update-status", "正在下载...");

// preload 暴露监听
contextBridge.exposeInMainWorld("api", {
  onStatus: (cb) => ipcRenderer.on("update-status",
    (event, status) => cb(status)),
});

// 渲染进程使用
window.api.onStatus((status) => {
  document.getElementById("status").textContent = status;
});

安全注意

1. 用 contextBridge 暴露(不直接暴露 ipcRenderer)
2. 校验事件参数(防注入)
3. 白名单通道名
4. 不信任渲染进程输入

Electron 渲染进程

Electron 渲染进程

渲染进程负责界面显示,本质是 Chromium 页面,可配合任意前端框架。

渲染进程能力

HTML/CSS/JavaScript 全部 Web 能力
DOM 操作、Canvas、WebGL
fetch/XHR 网络请求
localStorage/IndexedDB 本地存储
(默认无 Node 能力,需 preload 桥接)

配合前端框架

// React
npm create vite@latest renderer -- --template react

// Vue
npm create vue@latest renderer

// 构建后由主进程加载
win.loadFile("renderer/dist/index.html");

preload 桥接模式(推荐)

// preload.js
const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("electronAPI", {
  openDialog: () => ipcRenderer.invoke("open-dialog"),
  saveData: (data) => ipcRenderer.invoke("save-data", data),
});

// 渲染进程: 通过 window.electronAPI 使用
// 不直接接触 Node/ipc,安全且清晰

渲染进程中的网络请求

// CORS 限制: 渲染进程 fetch 有同源策略
// 跨域请求建议经主进程代理
ipcMain.handle("fetch-json", async (e, url) => {
  const resp = await fetch(url);
  return resp.json();
});

多页面与路由

单窗口多页面: hash 路由(常用)
多窗口: 每个窗口独立渲染进程
iframe: 可嵌入(注意隔离)

渲染进程性能

避免过多 DOM 节点
大数据用虚拟列表
Web Worker 处理重计算
GPU 加速: 默认开启

Electron 文件系统

Electron 文件系统

主进程通过 Node.js 的 fs 模块访问文件系统,渲染进程经 IPC 调用。

读取文件

const fs = require("fs/promises");

ipcMain.handle("read-file", async (e, path) => {
  try {
    const content = await fs.readFile(path, "utf-8");
    return { ok: true, content };
  } catch (err) {
    return { ok: false, error: err.message };
  }
});

写入文件

ipcMain.handle("write-file", async (e, path, content) => {
  await fs.writeFile(path, content, "utf-8");
  return { ok: true };
});

目录操作

const fs = require("fs/promises");

await fs.mkdir(dir, { recursive: true });   // 建目录
const entries = await fs.readdir(dir);       // 列目录
await fs.rename(oldPath, newPath);           // 重命名
await fs.unlink(path);                       // 删除文件
await fs.rmdir(dir, { recursive: true });    // 删除目录

应用数据目录

const { app } = require("electron");
const path = require("path");

// 用户数据目录(跨平台自动定位)
const userData = app.getPath("userData");
// macOS: ~/Library/Application Support/应用名
// Win:   %APPDATA%/应用名
// Linux: ~/.config/应用名

const configPath = path.join(userData, "config.json");
await fs.writeFile(configPath, JSON.stringify(config));

拖放文件

// 渲染进程
document.addEventListener("drop", (e) => {
  e.preventDefault();
  const filePath = e.dataTransfer.files[0].path;
  window.api.readFile(filePath);   // IPC 到主进程
});
document.addEventListener("dragover", (e) => e.preventDefault());

安全注意

文件路径白名单校验
防止路径穿越(../)
大文件流式处理(fs.createReadStream)

Electron 网络请求

Electron 网络请求

Electron 应用发起网络请求的方式与注意事项。

渲染进程 fetch(有 CORS 限制)

// 页面内请求受同源策略限制
const resp = await fetch("https://api.example.com/data");
const data = await resp.json();

// 跨域请求会被 CORS 拦截
// 方案: 主进程代理 / 关闭 webSecurity(不推荐)

主进程请求(Node 环境,无 CORS)

// 主进程用 Node 原生 fetch 或 axios
const resp = await fetch("https://api.example.com/data");
const data = await resp.json();

// 暴露给渲染进程
ipcMain.handle("api-get", async (e, url) => {
  const resp = await fetch(url);
  return resp.json();
});

axios 主进程封装

npm install axios

const axios = require("axios");

ipcMain.handle("http-request", async (e, { url, method = "get", data }) => {
  try {
    const resp = await axios({ url, method, data });
    return { ok: true, data: resp.data };
  } catch (err) {
    return { ok: false, error: err.message };
  }
});

下载文件

const { session } = require("electron");

// 监听下载
session.defaultSession.on("will-download", (event, item) => {
  item.setSavePath("/path/to/save/file.zip");
  item.on("updated", (e, state) => {
    if (state === "progressing") {
      const pct = item.getReceivedBytes() / item.getTotalBytes();
      console.log(`下载进度: ${(pct * 100).toFixed(1)}%`);
    }
  });
  item.once("done", (e, state) => {
    console.log(state === "completed" ? "完成" : "取消");
  });
});

win.webContents.downloadURL("https://example.com/file.zip");

请求头与代理

// 修改请求头
session.defaultSession.webRequest.onBeforeSendHeaders(
  (details, callback) => {
    details.requestHeaders["User-Agent"] = "MyApp/1.0";
    callback({ requestHeaders: details.requestHeaders });
  }
);

Electron 数据库

Electron 数据库

桌面应用常选用 SQLite 或文件型数据库进行本地数据存储。

方案选择

SQLite: 单文件数据库,桌面应用首选
  (better-sqlite3 同步高效 / sql.js WASM)
低负载: JSON 文件 / electron-store
复杂查询: SQLite / NeDB(已弃用)
同步需求: 客户端离线优先架构

better-sqlite3 使用

npm install better-sqlite3

const Database = require("better-sqlite3");
const db = new Database(path.join(app.getPath("userData"), "app.db"));

// 建表
db.exec(`
  CREATE TABLE IF NOT EXISTS notes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT,
    content TEXT,
    created_at TEXT DEFAULT (datetime('now'))
  )
`);

// 插入
const stmt = db.prepare("INSERT INTO notes(title, content) VALUES (?, ?)");
const info = stmt.run("第一篇", "内容...");
console.log(info.lastInsertRowid);

// 查询
const rows = db.prepare("SELECT * FROM notes").all();
const row = db.prepare("SELECT * FROM notes WHERE id = ?").get(1);

// 更新/删除
db.prepare("UPDATE notes SET content = ? WHERE id = ?").run("新内容", 1);
db.prepare("DELETE FROM notes WHERE id = ?").run(1);

electron-store(简单键值)

npm install electron-store

const Store = require("electron-store");
const store = new Store({ name: "settings" });

store.set("theme", "dark");
console.log(store.get("theme"));    // dark
store.delete("theme");

数据安全

1. 敏感数据加密存储(如 electron-safeStorage)
2. 数据库备份(定期复制 db 文件)
3. 事务保证一致性
4. WAL 模式提升性能与可靠性

Electron 调试

Electron 调试

系统掌握 Electron 主进程与渲染进程的调试方法。

渲染进程调试(DevTools)

// 自动打开开发者工具
win.webContents.openDevTools({ mode: "right" });

// 渲染进程 console 输出
console.log("渲染进程日志");   // 显示在 DevTools Console

主进程调试(终端 + VS Code)

// 终端直接看 console 输出
console.log("主进程日志");   // 打印到终端

// VS Code: launch.json 配置(见开发工具章节)
// 断点调试主进程代码

日志级别与文件

// 简单日志封装
const fs = require("fs");
const path = require("path");

function log(level, msg) {
  const line = `[${new Date().toISOString()}] [${level}] ${msg}
`;
  fs.appendFileSync(path.join(app.getPath("userData"), "app.log"), line);
  console.log(line.trim());
}

log("info", "应用启动");
log("error", "发生错误");

崩溃与错误监控

// 未捕获异常
process.on("uncaughtException", (err) => {
  log("error", `未捕获异常: ${err.stack}`);
});

// 渲染进程崩溃
win.webContents.on("render-process-gone", (e, details) => {
  log("error", `渲染进程崩溃: ${details.reason}`);
  win.reload();   // 自动恢复
});

// 主进程崩溃后自动重启(生产)
app.on("child-process-gone", (e, details) => {
  log("error", `子进程退出: ${details.reason}`);
});

性能分析

win.webContents.on("did-finish-load", () => {
  // 内存使用
  console.log(process.memoryUsage());
});

// Chrome 任务管理器(DevTools → More tools → Task manager)
// Performance 面板录制分析

Electron 打包发布

Electron 打包发布

把应用打包成安装包并分发到各平台。

工具选择

electron-builder: 功能全,支持多平台安装包
electron-forge:  官方推荐,与打包发布一体化
electron-packager: 简单打包目录(无安装包)

electron-builder 配置

npm install -D electron-builder

// package.json
{
  "build": {
    "appId": "com.example.myapp",
    "productName": "我的应用",
    "directories": { "output": "dist" },
    "files": ["main.js", "preload.js", "renderer/**/*"],
    "win": {
      "target": ["nsis"],       // 安装包
      "icon": "build/icon.ico"
    },
    "mac": {
      "target": ["dmg", "zip"],
      "category": "public.app-category.developer-tools",
      "icon": "build/icon.icns"
    },
    "linux": {
      "target": ["AppImage", "deb"],
      "icon": "build/icon.png"
    }
  },
  "scripts": {
    "dist": "electron-builder",
    "dist:win": "electron-builder --win",
    "dist:mac": "electron-builder --mac",
    "dist:linux": "electron-builder --linux"
  }
}

打包命令

npm run dist              # 当前平台
npm run dist:win          # Windows(可在 mac 交叉打包)
npm run dist:mac          # macOS(仅 mac)
npm run dist:linux        # Linux

# 产物在 dist/ 目录
# Windows: 生成 NSIS 安装器 .exe
# macOS:   .dmg
# Linux:   .AppImage / .deb

图标要求

Windows: .ico(至少256x256)
macOS:   .icns(含多尺寸)
Linux:   .png(512x512)

生成工具: electron-icon-builder
npm install -D electron-icon-builder

发布渠道

Windows: GitHub Releases / 官网 / 应用商店
macOS:   App Store / 公证(notarization)后分发
Linux:   AppImage/Flatpak/Snap 商店
自动更新: electron-updater / Sparkle(mac)

Electron 安全性

Electron 安全性

桌面应用安全同样重要,遵循官方安全清单可避免大部分漏洞。

安全清单(官方推荐)

1. 只加载可信内容(https)
2. 禁用 nodeIntegration
3. 启用 contextIsolation
4. 所有 IPC 校验参数
5. 使用 preload 暴露最小 API
6. 设置 CSP 内容安全策略
7. 处理未知内容时禁用 webview

默认安全配置

new BrowserWindow({
  webPreferences: {
    contextIsolation: true,    // 必须
    nodeIntegration: false,    // 必须
    sandbox: true,             // 渲染进程沙箱
    webSecurity: true,         // 默认开启
  },
});

IPC 安全

// 校验来源与参数
ipcMain.handle("delete-file", (event, filePath) => {
  // 校验 filePath 是否在允许目录内
  const allowed = path.resolve(app.getPath("userData"));
  const target = path.resolve(filePath);
  if (!target.startsWith(allowed)) {
    throw new Error("路径不允许");
  }
  // 执行删除
});

// 验证发送者
const validSender = (event) =>
  event.senderFrame.url.startsWith("https://myapp.com");

CSP(内容安全策略)

<meta http-equiv="Content-Security-Policy"
  content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">

// 禁止内联脚本与外部来源,降低 XSS 风险

其他安全实践

1. 禁用打开新窗口(设置 window.open 拦截)
2. 证书校验(不忽略 HTTPS 错误)
3. 敏感数据用 safeStorage 加密
4. 及时更新 Electron 版本(修复 CVE)
5. 审查第三方依赖(supply chain)

Electron 自动更新

Electron 自动更新

实现应用自动更新,让用户始终使用最新版本。

electron-updater 方案

npm install electron-updater

// main.js
const { autoUpdater } = require("electron-updater");

app.whenReady().then(() => {
  // 检查更新(不自动下载)
  autoUpdater.checkForUpdatesAndNotify();

  // 事件
  autoUpdater.on("checking-for-update", () => {
    log("正在检查更新...");
  });

  autoUpdater.on("update-available", (info) => {
    log("发现新版本: " + info.version);
  });

  autoUpdater.on("update-not-available", () => {
    log("已是最新版本");
  });

  autoUpdater.on("download-progress", (progress) => {
    console.log(`下载: ${progress.percent.toFixed(1)}%`);
  });

  autoUpdater.on("update-downloaded", () => {
    // 提示用户重启安装
    dialog.showMessageBox({
      type: "info",
      message: "更新已下载,重启应用完成更新?",
      buttons: ["现在重启", "稍后"],
    }).then(({ response }) => {
      if (response === 0) autoUpdater.quitAndInstall();
    });
  });

  autoUpdater.on("error", (err) => {
    log("更新错误: " + err.message);
  });
});

发布配置(与 electron-builder 配合)

// electron-builder 配置发布源
"publish": {
  "provider": "github",          // 或 generic(自建服务器)
  "owner": "username",
  "repo": "myapp"
}

// 生成 latest.yml(更新元数据)
// 上传安装包 + latest.yml 到发布地址

通用发布源(自建)

"publish": {
  "provider": "generic",
  "url": "https://updates.example.com/app/"
}
// 上传 dist 产物到该 URL

注意

1. macOS 需代码签名与公证才能自动更新
2. Windows 需代码签名(可选,但强烈建议)
3. 更新失败需回滚机制
4. 测试更新流程(可用 electron-updater 的 dev-app-update.yml)

Electron 总结与进阶

Electron 总结与进阶

恭喜完成 Electron 教程!回顾核心知识并给出进阶方向。

知识回顾

  • 架构:主进程(Node) + 渲染进程(Chromium) + preload 桥接
  • 基础:安装、Hello World、开发工具、核心模块
  • 界面:窗口、菜单、对话框、托盘
  • 通信:IPC(invoke/handle、send/on)、contextBridge
  • 能力:文件系统、网络请求、SQLite 数据库
  • 工程:调试、打包(electron-builder)、安全、自动更新

进阶方向

  1. 框架集成:React/Vue + Vite + Electron 完整工程化
  2. 性能优化:进程调度、内存管理、启动加速
  3. 多窗口架构:复杂应用的多窗口状态管理
  4. 原生模块:Node 原生插件(native addons)
  5. 与 Tauri 对比:了解 Rust 方案(更小更快)的取舍

实践建议

1. 用 electron-forge 初始化项目
2. 先小工具练手(笔记/剪贴板/文件管理)
3. 严格遵循安全清单
4. 参考 VS Code 等开源项目架构
5. 关注 electronjs.org 官方文档与示例

参考资料

官网: electronjs.org
文档: electronjs.org/docs
示例: electron/electron-quick-start
社区: Electron 中文社区 / GitHub Discussions

Electron 让 Web 开发者轻松进入桌面应用领域,掌握主/渲染进程模型与安全实践,即可构建专业级桌面软件。

第 1 / 21 节