6月5日 19:59

Electron 架构详解:Chromium + Node.js 如何协同工作

Electron 让你用 JavaScript、HTML、CSS 写桌面应用——VS Code、Discord、Slack 都是它做的。它不是什么黑科技,本质就是把 Chromium(浏览器内核)和 Node.js(服务端运行时)打包在一起,让 Web 页面能调用系统级 API。

三层架构

Electron 的架构可以拆成三层来看:

shell
┌──────────────────────────────────────────────┐ │ 你的应用代码 │ ├──────────────────┬───────────────────────────┤ │ Chromium │ Node.js │ (渲染 UI)(系统 API、文件、网络)├──────────────────┴───────────────────────────┤ │ 操作系统 (Windows / macOS / Linux)└──────────────────────────────────────────────┘
  • Chromium:负责把 HTML/CSS/JS 渲染成用户看到的界面。Electron 内嵌了完整的 Chromium,所以你的应用就是一个独立的浏览器窗口
  • Node.js:负责和操作系统打交道——读写文件、创建子进程、访问网络、操作剪贴板等。Electron 内嵌了完整的 Node.js 运行时
  • 你的代码:运行在上述两个环境之上,通过 Electron 提供的 API 把两者串联起来

这就是 Electron 的核心卖点:一个代码库同时拥有浏览器的渲染能力和 Node.js 的系统能力,编译一次就能跑在 Windows、macOS、Linux 上。

多进程模型

Electron 继承了 Chromium 的多进程架构。这不是随意设计——Chromium 之所以用多进程,是因为单个网页崩溃不应该拖垮整个浏览器。Electron 同理:一个窗口崩了,其他窗口和主进程还能继续工作。

shell
主进程 (Node.js) ├── 渲染进程 1 (Chromium) → 窗口 A ├── 渲染进程 2 (Chromium) → 窗口 B ├── 渲染进程 3 (Chromium) → 窗口 C └── GPU 进程 (共享)

主进程

  • 应用入口,package.jsonmain 字段指向的文件
  • 运行在 Node.js 环境中,可以使用 fspathchild_process 等所有 Node.js 模块
  • 负责创建 BrowserWindow、管理应用生命周期(app.whenReady()app.quit()
  • 处理原生对话框、系统菜单、托盘图标

渲染进程

  • 每个 BrowserWindow 对应一个独立的渲染进程
  • 运行在 Chromium 环境中,就是一个完整的网页运行环境
  • 默认不能直接使用 Node.js API(安全原因),需要通过 preload 脚本桥接

GPU 进程

  • Chromium 自动管理,负责图形渲染(硬件加速)
  • 一般不需要手动干预,但如果 GPU 加速导致问题,可以通过 app.disableHardwareAcceleration() 关闭

两个运行时怎么协作

Chromium 和 Node.js 各自有一套事件循环。Electron 在底层把它们整合到了一起:

  • Node.js 的 libuv 事件循环Chromium 的消息循环 被合并,两个环境的 I/O 和定时器可以互不阻塞地运行
  • 你可以在同一个 JavaScript 上下文中既用 setTimeout(浏览器 API)又用 fs.readFile(Node.js API)

但在渲染进程中,出于安全考虑,这种融合是被限制的——contextIsolation 默认开启,Node.js API 被隔离。你只能通过 preload 脚本和 IPC 通信来间接使用系统能力。

IPC 通信机制

主进程和渲染进程之间通过 IPC(进程间通信)传递消息:

javascript
// 主进程 ipcMain.handle('read-config', async () => { const data = await fs.promises.readFile('config.json', 'utf-8') return JSON.parse(data) }) // 渲染进程(通过 preload 暴露的 API) const config = await window.electronAPI.readConfig()

通信是异步的,数据经过结构化克隆序列化——所以不能传函数、DOM 节点或循环引用的对象。

Electron 的优势和代价

优势

  • 跨平台:一套代码跑三个系统,开发成本远低于分别写原生应用
  • 前端友好:会写网页就能写桌面应用,团队不需要学 Swift / C# / Qt
  • 生态丰富:npm 上所有包都能用,Web 社区的 UI 库(React、Vue)直接拿来用
  • 自动更新:内置 autoUpdater,不需要自己实现增量更新

代价

  • 包体积大:内嵌 Chromium + Node.js,最小打包也要 100MB+
  • 内存占用高:每个渲染进程就是一个 Chromium 标签页,开多窗口内存涨得很快
  • 性能不如原生:JS 的执行效率和内存管理比 C++/Rust 差,CPU 密集型任务会卡顿
  • 启动慢:冷启动需要加载 Chromium 和 Node.js,比原生应用慢

这些代价不是"优化一下就好了"——它们是 Electron 架构的固有特征。如果你的应用对包体积、内存或启动速度有硬性要求(比如轻量工具类应用),Electron 可能不是最佳选择。Tauri(Rust + WebView)和 Wails(Go + WebView)是更轻量的替代方案。

什么时候该用 Electron

适合不适合
内容型应用(编辑器、笔记、聊天)轻量工具(计算器、截图)
需要丰富 UI 的桌面端对启动速度极敏感
团队是前端为主需要极低内存占用
需要快速跨平台上线大量原生系统集成

Electron 最大的价值是降低桌面应用的开发门槛。如果团队已经会写 Web 应用,Electron 能让你们在几周内交付一个可用的桌面端——这个效率优势是原生开发无法比拟的。

标签:Electron