Electron.js 从入门到精通 · 完整学习指南

chat
chat

路线清晰 · 结构成体系 · 强调最佳实践
适用版本:Electron 20+(当前稳定线 Electron 43/44,Chromium 150 · Node 24 · V8 15)
最后校验:2026-08


0. 如何使用这份指南

适用人群
- 有 HTML / CSS / JavaScript 基础,想用 Web 技术做桌面应用的前端/全栈开发者
- 需要把现有 Web 项目打包成跨平台桌面端(macOS / Windows / Linux)

前置知识(建议先掌握)
- JavaScript(ES2020+)、Node.js 基础(CommonJS / ESM、npm、文件路径)
- 任意前端框架其一(React / Vue / Svelte 均可,本指南以 Vite 体系为例)
- 计算机常识:进程、主线程、文件系统、HTTP/HTTPS、数字签名(概念即可)

阅读建议
本指南按"概念 → 机制 → 能力 → 工程化 → 精通"五阶段递进,后一阶段依赖前一阶段的概念。建议按顺序学,把每阶段的"最小可运行示例"亲手敲一遍,而非只看。每阶段末尾有「最佳实践」小结,可直接当 checklist 用。


1. 学习路线图总览(Roadmap)

五个阶段,建议 8 周(每天 1–2 小时)走完。阶段之间有强依赖,箭头表示「必须先理解」:

1 入门基础
架构 + Hello World

2 核心机制
IPC 通信

3 桌面能力
窗口/菜单/存储

4 工程化
打包/更新/签名

5 精通
安全/性能/测试

箭头表示「必须先理解」的依赖关系:1→2→3,2、3→4,2、3、4→5。

阶段目标速览

阶段 核心目标 关键产出 预计周期
一 · 入门基础 建立"主进程 / 渲染进程 / 预加载"的心智模型 跑通第一个窗口应用 1 周
二 · 核心机制 掌握 IPC 与安全通信边界 主进程↔渲染进程双向通信示例 1.5 周
三 · 桌面能力 用好窗口、菜单、存储、原生 API 带菜单/托盘/本地存储的小工具 2 周
四 · 工程化 脚手架、打包、自动更新、签名 可分发安装包 + 自动更新 2 周
五 · 精通 安全、性能、测试、调试、架构 生产级可维护项目 1.5 周

2. 阶段一:入门基础

2.1 Electron 是什么 / 不是什么

是什么:用 HTML/CSS/JS 构建跨平台桌面应用的运行时。本质是「Chromium(渲染)+ Node.js(系统能力)」的组合,由同一个 V8 引擎驱动。

不是什么
- ❌ 不是浏览器——你的代码拥有文件系统、shell、注册表等完整系统权限
- ❌ 不是跨平台"一次编写处处相同"的银弹——各平台有原生差异(菜单、托盘、签名、权限)
- ❌ 不适合处理完全不可信的远程内容(见阶段五安全章)

2.2 核心架构:三个角色

主进程 (main process) — 有完整系统权限

app 生命周期
创建 BrowserWindow
系统 API:文件/菜单/托盘
ipcMain 接收请求

↓ loadFile / loadURL
预加载 (preload.js) — 隔离的 JS context,唯一受控桥

contextBridge 暴露安全 API
受限 Node 子集

↓ 隔离的 JS context
渲染进程 (renderer) — 默认沙箱,无 Node.js

你的 Web UI
React / Vue 等

渲染进程经 preload 的 ipcRenderer 向主进程发起请求;主进程通过 ipcMain 处理并把结果回传。

  • 主进程(Main):应用入口(main.js),一个应用只有一个。负责创建窗口、调用系统 API、管理生命周期。
  • 渲染进程(Renderer):每个 BrowserWindow 里的网页,就是一个渲染进程。从 Electron 20 起默认运行在沙箱中,不能直接用 Node.js
  • 预加载脚本(Preload):在渲染进程加载前注入,运行在「隔离的 JS context」。它是连接渲染进程(不可信)与主进程(可信)的唯一受控桥

⚠️ 心智模型一句话:渲染进程 = 受限制的网页;主进程 = 有系统权限的后台服务;preload = 两者之间的安检门。

2.3 环境准备

# Node 18+(建议 20+),npm 9+
node -v
npm init -y
npm install --save-dev electron
# 验证安装
npx electron --version   # 应输出版本号(如 v43.x)

2.4 第一个 Hello World

package.json

{
  "name": "hello-electron",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": { "start": "electron ." }
}

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'),
      // 以下三项从 Electron 20 起已是默认值,显式写出便于理解
      contextIsolation: true,
      nodeIntegration: false,
      sandbox: true,
    },
  });
  win.loadFile('index.html');
}

app.whenReady().then(createWindow);

index.html(渲染进程)

<!DOCTYPE html>
<html>
  <body>
    <h1>Hello Electron 👋</h1>
    <p>来自主进程的版本:<span id="ver"></span></p>
    <script src="renderer.js"></script>
  </body>
</html>

preload.js(预加载)

const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
  getVersion: () => ipcRenderer.invoke('get-version'),
});

renderer.js

const ver = await window.api.getVersion();
document.getElementById('ver').textContent = ver;

main.js 里补充 IPC 处理

const { ipcMain } = require('electron');
ipcMain.handle('get-version', () => process.versions.electron);

运行:npm start

2.5 阶段一最佳实践

  • ✅ 始终显式声明 webPreferences(contextIsolation / nodeIntegration / sandbox / preload),即使它与默认值相同——可读性与安全意图清晰。
  • ✅ 入口文件命名用 main.js,与 package.jsonmain 字段一致。
  • ❌ 不要在渲染进程里写 require('electron')nodeIntegration: true
  • ✅ 用 app.whenReady() 而非直接调用,避免窗口在 app 未就绪时创建。

3. 阶段二:核心机制(IPC 进程间通信)

3.1 为什么必须 IPC

渲染进程在沙箱里,没有 Node.js、没有系统权限。任何"读文件、写配置、调用系统 API"的需求,都要委托主进程完成。这就是 IPC(Inter-Process Communication)。

3.2 通信原语对照

场景 渲染→主 主→渲染 主进程侧 渲染侧(经 preload)
请求/响应(双向) invoke(channel, ...args) —— ipcMain.handle(channel, handler) ipcRenderer.invoke → Promise
单向通知 send(channel, ...args) —— ipcMain.on(channel, listener) ipcRenderer.send
主推事件 —— webContents.send(channel, ...args) win.webContents.send ipcRenderer.on(channel, listener)

3.3 preload + contextBridge(安全桥)

// preload.js —— 只暴露「具体、经过校验」的 API,绝不透传整个 ipcRenderer
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('fs', {
  readNote: (name) => ipcRenderer.invoke('note:read', name),
  saveNote: (name, content) => ipcRenderer.invoke('note:save', name, content),
  onUpdate: (cb) => ipcRenderer.on('note:updated', (_e, d) => cb(d)),
});

渲染进程里就能 await window.fs.readNote('todo')完全接触不到 ipcRenderer 本身

3.4 最常用模式:invoke / handle

// 主进程
ipcMain.handle('note:save', async (event, name, content) => {
  // 见阶段五:此处必须做类型/路径校验
  const safeName = path.basename(name);
  const full = path.join(app.getPath('userData'), safeName);
  await fs.writeFile(full, content);
  return { ok: true };
});
// 渲染进程(经 preload 暴露)
const res = await window.fs.saveNote('todo', '买牛奶');

3.5 阶段二最佳实践

  • ✅ preload 只 exposeInMainWorld 具体函数,绝不做 contextBridge.exposeInMainWorld('electron', { ipcRenderer })(等于把整把钥匙交出去)。
  • ✅ 主进程对所有 IPC 入参二次校验(纵深防御),不信渲染端已校验。
  • ✅ 用 invoke/handle 取代旧的 send/on 做请求响应,天然返回 Promise,错误处理更干净。
  • ✅ 用 event.senderFrame / 来源校验来确认消息发送方可信(阶段五详述)。

4. 阶段三:桌面能力

4.1 BrowserWindow 深入

const win = new BrowserWindow({
  width: 1000, height: 700,
  frame: false,            // 无边框(自定义标题栏需自己画)
  transparent: false,
  resizable: true,
  webPreferences: { preload, contextIsolation: true, nodeIntegration: false },
});
win.loadURL('https://app.example.com'); // 远程内容务必 HTTPS + 关闭 nodeIntegration

常用能力:win.webContents.openDevTools()win.on('closed')win.setAlwaysOnTop(true)new BrowserWindow({ parent: win, modal: true })(子窗口/弹窗)。

4.2 应用生命周期(app 事件)

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit(); // macOS 习惯保留 dock 图标
});
app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) createWindow(); // macOS 点击 dock 重建窗口
});

4.3 菜单 / 托盘 / 快捷键

  • Menu.buildFromTemplate([...]) + Menu.setApplicationMenu(menu)(macOS 顶部菜单 / Windows 顶部菜单)
  • 右键菜单:win.webContents.on('context-menu', ...) 配合 menu.popup()
  • Tray:系统托盘常驻(后台应用标配)
  • globalShortcut.register('CommandOrControl+Shift+K', ...):全局快捷键

4.4 系统集成 API

  • 对话框:dialog.showOpenDialog / showSaveDialog / showMessageBox
  • 通知:new Notification({ title, body })(注意 Electron 43 起 macOS 支持 remove()/removeAll() 等精细管理)
  • 剪贴板:clipboard.writeText() / readText()
  • 打开外部:shell.openExternal(url)绝不对不可信内容使用,见安全章)
  • 打开文件:shell.openPath(path)

4.5 存储与持久化

需求 推荐方案
轻量配置 / 用户偏好 electron-store(基于 JSON,最简单)
结构化数据 / 大量记录 better-sqlite3(本地 SQLite,原生模块)
密钥 / Token safeStorage(主进程加密,绝不放渲染进程)
大文件 / 用户文档 直接用 fs + dialog 让用户选路径
// 主进程用 safeStorage 存敏感信息
const { safeStorage } = require('electron');
const encrypted = safeStorage.encryptString('my-secret-token');
// 解密:safeStorage.decryptString(encrypted)

4.6 协议与文件系统

  • 自定义协议:protocol.registerSchemesAsPrivileged + protocol.handle('app', ...)(比 file:// 更安全,避免 file 协议的安全限制)
  • 拖拽文件进窗口:webContents.on('will-navigate') 拦截 + 处理 electron:// 深链

4.7 阶段三最佳实践

  • ✅ 远程内容(loadURL)必须 HTTPS + nodeIntegration: false + contextIsolation: true
  • ✅ 敏感凭证只在主进程用 safeStorage,渲染端永远拿不到明文。
  • ✅ macOS 遵循「窗口全关也不退出」的交互习惯。
  • ✅ 托盘/后台应用要明确退出入口,避免用户"关不掉"。

5. 阶段四:工程化与分发

5.1 脚手架选型(官方推荐 Electron Forge)

npm init electron-app@latest my-app

Electron Forge 是官方维护的全流程工具链:脚手架 + 开发调试 + 打包 + 签名 + 发布一键搞定,新手首选。

5.2 打包工具对比与选型

工具 定位 何时选
Electron Forge 官方全流程(脚手架+打包+发布) 新项目、想少折腾
electron-builder 配置极度灵活、格式最全 复杂分发、CI/CD、高度定制(dmg/nsis/appx/snap)
electron-vite 与 Vite 深度集成、HMR 极快 现代前端框架(React/Vue/Svelte)项目
electron-packager 仅打包不生成安装器 快速原型 / 内部分发

选型经验:初期用 Forge 快速起步;进入生产发布、需要精细定制安装包/自动更新服务端时,迁到 electron-builder 很常见。现代前端项目直接 electron-vite 体验最好。

5.3 与前端框架集成(Vite + React 示例)

npm create electron-vite@latest
# 选 react-ts 模板

统一在 electron.vite.config.js 管理主进程 / 预加载 / 渲染进程三个入口,开发期热更新,构建期按需编译,包体更小、冷启动更快。

5.4 自动更新(electron-updater)

// 主进程
const { autoUpdater } = require('electron-updater');
app.whenReady().then(() => {
  autoUpdater.checkForUpdatesAndNotify();
});
// 在渲染端提示进度、让用户确认重启,避免打断操作

支持 GitHub Releases / S3 / 私有服务器;Windows 用 NSIS/Squirrel,macOS 用 Squirrel.Mac,Electron 41+ 还支持 MSIX 自动更新。

5.5 代码签名与公证

  • macOS:必须签名 + 公证(Notarization),否则 Gatekeeper 直接拦截。需 Apple Developer 证书 + hardened runtime + 正确的 entitlements
  • Windows:Authenticode 代码签名证书,否则 SmartScreen 报警。
  • Linux:一般无需签名,但 .deb/.rpm/AppImage 要注意依赖。
  • CI 里做签名(GitHub Actions / CircleCI),不要把证书私钥提交进仓库。

5.6 多窗口架构

  • 主窗口 + 设置窗口:new BrowserWindow({ parent: mainWin, modal: true })
  • 多独立窗口:用 BrowserWindow.getAllWindows() 管理,注意每个窗口都是一个渲染进程,IPC 要带 webContents 路由。
  • 复杂场景用 WebContentsView(新版)替代旧的 <webview>,性能和隔离更好。

5.7 阶段四最佳实践

  • ✅ 新项目直接用 npm init electron-app@latest,别手写 build 配置起步。
  • ✅ 签名/公证放 CI,证书用 secrets 注入,绝不进 git。
  • ✅ 自动更新不要静默强制重启,先提示、让用户保存工作。
  • ✅ 生产构建开启 asar(默认),Electron 41+ 可启用 ASAR Integrity 增强防篡改。

6. 阶段五:精通(安全 / 性能 / 测试 / 调试)

6.1 安全最佳实践(重中之重)

Electron 不是浏览器,渲染进程一旦被 XSS,攻击者可 require('child_process') 执行任意命令。三条安全支柱从 Electron 20 起默认开启,你的工作是不去关掉它们:

new BrowserWindow({
  webPreferences: {
    preload: path.join(__dirname, 'preload.js'),
    contextIsolation: true,   // ✅ 隔离 preload 与渲染进程
    nodeIntegration: false,   // ✅ 渲染进程无 Node.js
    sandbox: true,            // ✅ Chromium 沙箱
    webSecurity: true,         // ✅ 同源策略
  },
});

官方安全检查清单(必做项)
1. 只加载安全内容(远程用 HTTPS,不用 HTTP)
2. 远程内容绝不开启 nodeIntegration
3. 所有渲染进程开启 contextIsolation
4. 开启进程沙箱 sandbox: true
5. 加载远程内容的 session 设置 ses.setPermissionRequestHandler()
6. 关闭 webSecurity
7. 定义严格 CSP,例如 Content-Security-Policy: script-src 'self'
8. 不开 allowRunningInsecureContent / experimentalFeatures / enableBlinkFeatures
9. <webview> 不用 allowpopups,校验其 options/params
10. 禁用或限制导航(will-navigate 拦截)、限制新建窗口
11. 不对不可信内容用 shell.openExternal
12. 使用最新版 Electron
13. 校验所有 IPC 消息的发送方
14. 用自定义协议替代 file://
15. 检查可关闭的 Fuses(如 runAsNodenodeCliInspect

纵深防御:主进程二次校验示例

ipcMain.handle('note:save', async (event, filename, content) => {
  if (typeof filename !== 'string' || typeof content !== 'string')
    throw new Error('参数类型非法');
  const safeName = path.basename(filename);              // 防路径穿越
  const full = path.join(app.getPath('userData'), safeName);
  if (!full.startsWith(app.getPath('userData')))
    throw new Error('路径逃逸被拦截');
  if (content.length > 10 * 1024 * 1024) throw new Error('文件过大');
  await fs.writeFile(full, content);
  return { ok: true };
});

6.2 性能优化

  • 主进程轻量:重活(解压、大计算)放到 utility processutilityProcess.fork),避免阻塞 UI。
  • 渲染进程按需加载、代码分割(Vite/Rollup 天然支持)。
  • 控制 webPreferences 开销:能用 sandbox 就开,减少 preload 体积。
  • 大列表用虚拟滚动;避免渲染进程里跑长同步任务。
  • Electron 43 起主进程用 V8 启动快照、preload 编译为字节码缓存,启动更快——保持版本更新即自动受益。

6.3 测试(Spectron 已弃用)

工具 用途
Playwright E2E 测试(官方推荐替代 Spectron),可驱动真实窗口
Electron-Vitest 单元/集成测试,热更新快,现代项目首选
@electron/fuses 校验打包后的安全 fuse 配置
npm i -D @playwright/test
# 配置 test 指向编译后的 Electron 入口即可写 .spec.ts

6.4 调试

  • 渲染进程:窗口内 openDevTools(),或用 Vite 的浏览器 DevTools。
  • 主进程:
  • VS Code:launch.json"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron", "args": ["."]
  • 或命令行 electron --inspect=5858 .,Chrome 打开 chrome://inspect
  • preload 调试:Electron 43 起 preload 堆栈能显示正确文件路径和行号。

6.5 崩溃监控与稳定性

  • crashReporter 收集主进程/渲染进程崩溃堆栈(上报到自有服务)。
  • app.on('render-process-gone') / webContents.on('did-fail-load') 做兜底与重试。
  • 关键操作加 try/catch 与用户可见的错误提示,别让应用"静默死掉"。

6.6 架构模式与状态管理

  • 主进程 = 后端服务:只做系统能力 + 数据持久化 + IPC 路由。
  • 渲染进程 = 前端:用 React/Vue 的 store(Zustand / Pinia)管理 UI 状态,不要把主进程当数据库轮询
  • 跨窗口状态:用主进程做中转,或共享一个轻量 store;避免多个渲染进程各自持有一份真值。
  • 模块边界清晰:main/preload/renderer/ 三目录分离。

6.7 阶段五最佳实践

  • ✅ 安全默认项一个都别关;若要关,必须书面记录理由并加补偿措施。
  • ✅ 所有 IPC 入参主进程二次校验。
  • ✅ 重计算放 utility process,保持 UI 流畅。
  • ✅ 测试用 Playwright/Electron-Vitest,Spectron 已弃用不要学。
  • ✅ 崩溃上报 + 兜底处理,生产应用必备。

7. 最佳实践速查清单

[安全] contextIsolation: true / nodeIntegration: false / sandbox: true
[安全] 远程内容只用 HTTPS,且关闭 nodeIntegration
[安全] preload 只暴露具体 API,不透传 ipcRenderer
[安全] 主进程对 IPC 入参二次校验(类型/路径/大小)
[安全] 定义严格 CSP,校验 IPC 发送方
[架构] 主进程=后端,渲染进程=前端,preload=安检门
[工程] 新项目用 Electron Forge / electron-vite 起步
[工程] 签名+公证放 CI,证书走 secrets
[工程] 自动更新先提示再重启
[性能] 重活放 utility process,保持版本更新
[质量] Playwright / Electron-Vitest 做测试,crashReporter 监控

8. 常见陷阱 / 踩坑

  1. 在渲染进程里 require('electron') → 直接报错或安全漏洞。正确做法:经 preload + contextBridge。
  2. nodeIntegration: true 图省事 → 远程 XSS 即 RCE。阶段五安全章详述。
  3. shell.openExternal(untrustedUrl) → 可被钓鱼/执行危险协议。先白名单校验。
  4. <webview> 允许 allowpopups 且未校验 → 弹出不可信窗口。
  5. 主进程写死大量同步逻辑 → UI 卡顿。改用 utility process / async。
  6. 自动更新静默重启 → 用户数据丢失。先提示。
  7. 证书/私钥提交进 git → 安全事故。走 CI secrets。
  8. 用已弃用的 Spectron → 维护停滞。换 Playwright。
  9. file:// 协议直接加载 → 触发安全限制。改用自定义协议。
  10. 不更新 Electron 版本 → 已知漏洞未修。定期升级并审 security advisories。

9. 学习资源

  • 官方文档(最权威,必看):https://www.electronjs.org/zh/docs/latest
  • 安全:https://www.electronjs.org/zh/docs/latest/tutorial/security
  • IPC:https://www.electronjs.org/zh/docs/latest/tutorial/ipc
  • 沙箱:https://www.electronjs.org/zh/docs/latest/tutorial/sandbox
  • 官方示例库:https://github.com/electron/electron-quick-start
  • Electron Forge:https://www.electronforge.io/
  • electron-vite:https://github.com/alex8088/electron-vite
  • electron-builder:https://www.electron.build/
  • 社区模板:electron-react-boilerplate、electron-vue-vite(GitHub 搜索最新版)
  • 发布博客(跟进版本与安全变更):https://www.electronjs.org/zh/blog

10. 八周学习计划(按周拆解)

主题 动手任务 验收标准
1 阶段一:架构 + Hello World 搭环境,跑通窗口;画一张进程关系图 能解释主/渲染/preload 三者职责
2 阶段二:IPC 用 invoke/handle + preload 做「记事本」读写 渲染进程经安全桥读写本地文件
3 阶段三(上):窗口/菜单/托盘 做带菜单栏+托盘的最小工具 菜单/托盘/快捷键可用
4 阶段三(下):存储/系统集成 接入 electron-store + 通知 + 对话框 配置持久化,能弹通知
5 阶段四(上):脚手架 + 打包 用 Forge/electron-vite 重构,产出安装包 三平台安装包生成
6 阶段四(下):自动更新 + 签名 配 electron-updater + CI 签名 能推送更新、过 Gatekeeper
7 阶段五(上):安全 + 性能 审一遍安全检查清单,做纵深校验 安全默认项全开,主进程校验
8 阶段五(下):测试 + 调试 + 架构 写 Playwright E2E,配主进程调试 有测试、有崩溃上报、结构清晰

一句话总结:Electron 的门槛不在 API 多,而在「进程边界」和「安全意识」。把主进程当后端、渲染进程当前端、preload 当安检门,守住 contextIsolation / sandbox / nodeIntegration 三条线,你就已经超过了大部分生产级 Electron 应用的安全水位。

版权声明:
作者:东明兄
链接:https://blog.crazyming.com/note/3322/
来源:CrazyMing
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
海报
Electron.js 从入门到精通 · 完整学习指南
路线清晰 · 结构成体系 · 强调最佳实践 适用版本:Electron 20+(当前稳定线 Electron 43/44,Chromium 150 · Node 24 · V8 15) 最后校验:2026-08 0. 如……
<<上一篇
下一篇>>
chat