跳到主要内容

Electron

Electron 适合放在“前端团队想把 Web 技术直接带到桌面端”的那条线上看。它把 Chromium、Node.js 和桌面系统能力放在同一个框架里,让 HTML、CSS、JavaScript 不只跑在浏览器里,也能打包成真正的桌面应用。

很多团队第一次做桌面端,最终都会在 Electron 和 Tauri 之间做选择。Electron 更成熟,也更重。优点和代价都很明显。

一句话先讲清楚

Electron 不是“给网页套个窗口”这么简单。

它更像是:

  • 用 Chromium 渲染界面
  • 用 Node.js 和 Electron 主进程控制系统能力
  • 用 preload 和 IPC 把渲染层与系统层连起来
  • 最后打包成 macOS、Windows、Linux 的桌面应用

官方的 Why Electron 页面讲得很直接:Electron 把 Chromium、Node.js 以及接入原生能力的方式放在同一个框架里,目标就是构建功能完整的桌面应用。

适合什么场景

  • 已经有成熟的 Web 前端团队,希望进入桌面端
  • 应用本身就是复杂富客户端,而不是简单工具页
  • 需要稳定的跨平台桌面交付能力
  • 可以接受安装包体积偏大这件事

不太适合什么场景

  • 对安装包体积极度敏感
  • 只是想做一个很轻量的系统小工具
  • 团队很难持续维护桌面端安全边界

Electron 的核心结构

官方文档里最重要的两个概念就是:

  • main process
  • renderer process

1. Main process

主进程只有一个,负责:

  • 应用生命周期
  • 窗口创建
  • 原生菜单、托盘、对话框
  • 文件系统、系统级能力协调
import { app, BrowserWindow } from 'electron'
import path from 'node:path'

function createWindow() {
const win = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

win.loadURL('http://localhost:5173')
}

app.whenReady().then(() => {
createWindow()
})

2. Renderer process

每个窗口通常会有自己的 renderer process。它本质上就是一个 Chromium 页面环境,所以前端页面、组件、路由、状态管理基本都在这里。

这也是 Electron 对前端团队最友好的地方:大部分界面层工作,看起来就像平时写 Web 应用。

3. Preload

真正决定 Electron 项目质量高不高的,经常不是页面本身,而是 preload 写得怎么样。

preload 的作用是:

  • 运行在更高权限的上下文里
  • 负责把安全、有限、可控的 API 暴露给 renderer
  • 避免页面层直接拿到危险的 Node/Electron 能力
import { contextBridge, ipcRenderer } from 'electron'

contextBridge.exposeInMainWorld('desktop', {
openFile: () => ipcRenderer.invoke('dialog:openFile')
})

为什么 IPC 是 Electron 的主线

官方 IPC 教程说得很明确:因为 main process 和 renderer process 职责不同,很多能力只能通过 IPC 来完成。

这基本决定了 Electron 项目的日常结构:

  • 页面层发请求
  • 主进程执行系统动作
  • 结果再回到页面层
// main.ts
ipcMain.handle('dialog:openFile', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ['openFile']
})

if (canceled) return null
return filePaths[0]
})

// renderer.ts
const filePath = await window.desktop.openFile()

如果一个 Electron 项目把大量系统调用直接暴露给 renderer,后面通常会在安全和可维护性上出问题。

安全问题为什么总被反复强调

因为 Electron 天生权限很高。

页面层一旦拿到过多能力,风险会比普通 Web 页面大得多。官方安全文档和 context isolation 文档反复强调的主线,其实就几条:

  • 开启 contextIsolation
  • 不要把整个 ipcRenderer 直接暴露出去
  • 不要随便开启 nodeIntegration
  • preload 只暴露最小必要 API

官方文档还特别提到,contextIsolation 从 Electron 12 开始就是默认开启,而且这是推荐配置。

Electron 的优势到底是什么

1. 成熟

Electron 的最大优势不是轻,而是成熟。

  • 生态完整
  • 跨平台问题资料多
  • 打包、更新、调试、安装链路都比较成熟
  • 真正的大型桌面应用已经跑过很多年

2. Web 团队迁移成本低

对于 React、Vue、Vite 这类团队来说,界面层几乎可以无缝迁移。真正要补的主要是:

  • 进程模型
  • 桌面端权限边界
  • 打包与签名
  • 本地文件和系统能力调用

3. 系统能力广

Electron 能做的桌面事情很多:

  • 菜单
  • 托盘
  • 快捷键
  • 文件系统
  • 通知
  • 原生窗口
  • 多窗口管理

这让它很适合功能完整的桌面工具,而不只是展示型应用。

Electron 的代价也要讲清楚

1. 安装包体积大

这是最直观的代价。官方 Why Electron 页面也解释了原因:Electron 自带 Chromium、V8 和 Node.js 一起发布。

好处是环境一致,坏处就是安装包和内存占用通常都不小。

2. 内存模型更重

多进程架构、Chromium 渲染、桌面应用状态常驻,这几件事叠在一起,内存成本不会太低。

3. 安全边界需要自觉维护

Electron 不是默认“怎么写都安全”。它提供了工具,但边界要靠项目自己守住。

Electron 项目通常怎么组织

一个比较常见的结构是:

src/
main/
index.ts
ipc/
windows/
preload/
index.ts
renderer/
app/
pages/
components/
store/

这比把所有代码丢在一个 main.ts 和一个 renderer.ts 里稳得多。

可以简单理解成三层:

  • main:系统层
  • preload:受控桥接层
  • renderer:前端页面层

打包和工程工具怎么选

如果从头搭 Electron 项目,通常会先看这些:

  • Electron Forge
  • Electron Builder
  • Vite + Electron 的组合方案

大方向上:

  • 想要官方教程更顺:先看 Electron Forge
  • 想把前端构建和桌面打包拆得更细:再看更自由的组合方案

什么时候该选 Electron

可以优先考虑的情况:

  • 需要成熟桌面生态
  • 项目本身体量不小
  • 团队已经有成熟前端工程体系
  • 体积不是第一优先级

需要再想一层的情况:

  • 安装包必须尽量小
  • 只是做轻量桌面工具
  • 更看重系统 WebView 带来的体积优势

这时候 Tauri 往往也会进入候选。

Electron 和 Tauri 怎么看

先讲一个很实用的结论:

  • Electron 更成熟、更宽、更重
  • Tauri 更轻、更克制、更偏系统 WebView 路线

如果项目要求是“先把桌面端稳稳做出来,而且资料多、坑可查”,Electron 仍然很强。

如果更在意体积、内存和 Rust 侧能力,再去看 Tauri 会更自然。

我的看法

Electron 的价值不只是历史包袱大、生态老,而是它把桌面端工程这件事做成了一条非常成熟的路径。

很多时候,团队真正需要的不是“最轻”的方案,而是“最稳、最好查资料、最容易交付”的方案。Electron 在这点上仍然很有竞争力。

只要能接受体积和资源占用,它依然是桌面端前端工程里非常现实的一条路。