如果你正在为下一个桌面应用项目选择技术栈,面对 Electron、Tauri、Electro Bun 这些名字,是不是感觉有点“选择困难症”?尤其是当听到“Electron 太臃肿”、“Tauri 性能好但 Rust 难学”、“Electro Bun 是新秀”这些碎片化评价时,更不知道从何下手。
这篇文章要解决的,就是你的这个核心痛点。我们不做空泛的“技术选型指南”,而是通过一个真实的、可复现的“Hello World”应用,从零开始,在同一台机器上,用 Electron、Tauri 和 Electro Bun 分别实现完全相同的功能。然后,我们将从安装体验、开发效率、打包体积、内存占用、启动速度、跨平台一致性六个维度进行量化对比。
我的核心判断是:“Electron 已死”是个伪命题,但“一招鲜吃遍天”的时代确实过去了。选择哪个框架,不取决于哪个“最先进”,而取决于你的团队基因、项目生命周期和性能容忍度。一个需要快速验证的 MVP 和一个追求极致性能的桌面工具,答案截然不同。
读完本文,你将能:
- 清晰理解三大框架的核心差异与适用场景。
- 亲手完成三个框架的“Hello World”应用创建、开发与打包全流程。
- 获得一份基于实测数据的对比表格,为你的技术决策提供坚实依据。
- 避开各框架在安装、打包、生产部署中的常见“坑”。
1. 三大框架定位速览:谁解决什么问题?
在深入代码之前,我们必须先抛开技术细节,从“为什么存在”的角度理解它们。这决定了你的初始选择方向。
Electron:桌面应用的“开拓者”与“定义者”
- 核心原理:将 Chromium(浏览器)和 Node.js 打包成一个独立的桌面应用。你的前端代码(HTML/CSS/JS)运行在 Chromium 渲染进程中,而后端/系统交互逻辑则通过 Node.js 在主进程中执行。
- 核心优势:生态无敌。npm 上超过百万的包可直接使用。任何 Web 开发者都能几乎零成本上手,快速将 Web 应用“转换”为桌面应用。VSCode、Slack、Discord 等成功案例证明了其构建复杂应用的能力。
- 核心代价:体积与内存。每个 Electron 应用都携带一个完整的 Chromium,安装包动辄 100MB+,内存占用也相对较高。
Tauri:追求极致的“革新者”
- 核心原理:使用系统原生的 WebView(在 Windows 上是 WebView2,macOS 上是 WKWebView,Linux 上是 WebKitGTK)来渲染界面,后端逻辑使用 Rust 编写并编译为本地二进制文件。
- 核心优势:极致轻量。最终产物是真正的本地二进制文件 + 少量资源,安装包可以小到几 MB。性能好,内存占用低,安全性高(Rust 的内存安全特性)。
- 核心代价:学习曲线。需要接触 Rust(尽管基础使用门槛已被工具链大大降低),且其前端与后端的通信(通过
invoke)模式与 Electron 的ipc有所不同,生态虽在快速增长但远不及 npm。
Electro Bun:全栈开发的“新锐挑战者”
- 核心原理:基于 Bun(一个集运行时、包管理器、打包器、测试运行器于一身的 JavaScript 工具链)和 WebKit(通过
bun:ffi调用原生 API),旨在提供一个更现代、更集成的开发体验。 - 核心优势:开发体验与性能潜力。利用 Bun 极快的启动、安装和打包速度。旨在提供比 Electron 更轻量的替代方案,同时保持 JavaScript/TypeScript 的全栈开发体验。
- 核心代价:成熟度与生态。它是三者中最新的,API 和生态还在快速演变中,生产环境的风险相对较高,社区案例较少。
简单来说:
- 求快、求稳、团队是 Web 技术栈-> 优先看Electron。
- 求小、求快(运行时)、能接受新语言-> 重点看Tauri。
- 追求前沿工具链、愿意尝鲜、项目风险承受能力强-> 可以试试Electro Bun。
下面,我们就从零开始,用这三个框架构建同一个应用。
2. 环境准备:一次搞定所有依赖
为了保证对比的公平性,我们将在同一台机器(macOS/Linux/Windows 均可,本文以 macOS 为例,命令会注明差异)上进行所有操作。请确保你的环境满足以下要求:
2.1 基础环境
- Node.js:版本 18 或更高。这是 Electron 和 Electro Bun(Bun)的基础。推荐使用
nvm管理多版本。# 安装 nvm (macOS/Linux) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash # 安装并启用 Node.js 20 nvm install 20 nvm use 20 - Rust:Tauri 的必需环境。安装过程会同时安装
cargo(Rust 的包管理器)。
国内用户如果下载# 官方安装命令 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # 安装完成后,重启终端或运行 source $HOME/.cargo/envchannel-rust-stable.toml失败,可以配置国内镜像源(如中科大镜像)加速:# 编辑 ~/.cargo/config 文件 [source.crates-io] replace-with = 'ustc' [source.ustc] registry = "git://mirrors.ustc.edu.cn/crates.io-index" - Bun:Electro Bun 的运行时。
# 官方安装命令 curl -fsSL https://bun.sh/install | bash
2.2 框架 CLI 工具安装
我们将使用各框架官方的命令行工具来创建和管理项目。
- Electron:我们使用
electron-forge,它是一个完整的 Electron 开发、打包和发布工具链。npm install -g @electron-forge/cli - Tauri:使用
create-tauri-app或tauri-cli。# 使用 npm 初始化项目时,create-tauri-app 会自动安装 cli,也可单独安装 cargo install tauri-cli # 如果 cargo install 慢,可以用国内镜像或使用 npm 版本 npm install -g @tauri-apps/cli - Electro Bun:框架本身较新,我们直接使用 Bun 初始化项目并安装依赖。
2.3 验证安装
安装完成后,运行以下命令验证:
node --version # 应输出 v18.x 或更高 npm --version rustc --version # 应输出 rustc 1.xx.x cargo --version bun --version # 应输出 1.x.x electron-forge --version # 如果安装了 tauri-cli tauri --version环境就绪,让我们开始创建第一个应用。
3. 实战:创建“Hello World”应用
我们的目标应用非常简单:一个窗口,显示“Hello from [Framework Name]!”,并有一个按钮,点击后显示一个来自“后端”的消息。
3.1 Electron 实现
1. 创建项目:
mkdir electron-hello && cd electron-hello npm init -y npm install --save-dev electron @electron-forge/cli npx electron-forge importimport命令会为你配置好 Forge 的基本模板。
2. 项目结构:
electron-hello/ ├── package.json ├── src/ │ ├── index.html # 渲染进程页面 │ ├── index.js # 渲染进程脚本 │ └── main.js # 主进程脚本 └── forge.config.js # Electron Forge 配置3. 编写代码:
src/main.js- 主进程,创建窗口并加载页面。
const { app, BrowserWindow, ipcMain } = require('electron'); const path = require('path'); function createWindow() { const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js'), // 上下文隔离推荐使用 preload contextIsolation: true, // 启用上下文隔离,更安全 } }); mainWindow.loadFile(path.join(__dirname, 'index.html')); // 开发环境下打开开发者工具 // mainWindow.webContents.openDevTools(); } // 监听渲染进程的 `get-message` 请求,并返回数据 ipcMain.handle('get-message', () => { return `Hello from Electron Main Process! Time: ${new Date().toLocaleTimeString()}`; }); app.whenReady().then(() => { createWindow(); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });src/preload.js- 预加载脚本,在渲染进程上下文中安全地暴露 API。
const { contextBridge, ipcRenderer } = require('electron'); // 向渲染进程暴露一个安全的 `electronAPI` 对象 contextBridge.exposeInMainWorld('electronAPI', { getMessage: () => ipcRenderer.invoke('get-message') });src/index.html- 渲染进程页面。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Electron Hello</title> </head> <body> <h1>Hello from Electron!</h1> <button id="msgBtn">Get Message from Main Process</button> <p id="message"></p> <script src="./index.js"></script> </body> </html>src/index.js- 渲染进程逻辑。
document.getElementById('msgBtn').addEventListener('click', async () => { // 通过预加载脚本暴露的 API 调用主进程 const message = await window.electronAPI.getMessage(); document.getElementById('message').textContent = message; });4. 运行与打包:
# 开发运行 npm start # 打包应用 (生成在 out 目录) npm run make3.2 Tauri 实现
1. 创建项目:Tauri 推荐使用create-tauri-app,它会引导你选择前端框架。我们选择纯 HTML/JS 以保持对比公平。
# 使用 npm 创建 npm create tauri-app@latest # 按照提示操作: # - Project name: tauri-hello # - Frontend: Vanilla (HTML/CSS/JS) # - UI template: Vanilla # - Package manager: npm cd tauri-hello2. 项目结构:
tauri-hello/ ├── src-tauri/ # Rust 后端代码 │ ├── Cargo.toml # Rust 项目配置 │ ├── src/ │ │ └── main.rs # Rust 入口 │ └── build.rs ├── src/ # 前端代码 │ ├── index.html │ └── main.js ├── index.html └── package.json3. 编写代码:
src-tauri/src/main.rs- Rust 后端逻辑。
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // 在发布版本中隐藏 Windows 控制台 use tauri::Manager; // 定义一个 Rust 命令,可以被前端调用 #[tauri::command] fn get_message() -> String { format!("Hello from Tauri Backend (Rust)! Time: {:?}", chrono::Local::now().format("%H:%M:%S")) } fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![get_message]) // 注册命令 .run(tauri::generate_context!()) .expect("error while running tauri application"); }需要添加chrono依赖来格式化时间,编辑src-tauri/Cargo.toml:
[dependencies] tauri = { version = "1", features = ["shell-open"] } chrono = "0.4" # 新增依赖src/main.js- 前端逻辑。
import { invoke } from '@tauri-apps/api/tauri'; document.getElementById('msgBtn').addEventListener('click', async () => { // 调用 Rust 后端定义的 `get_message` 命令 const message = await invoke('get_message'); document.getElementById('message').textContent = message; });对应的index.html与 Electron 示例类似,将标题改为 “Hello from Tauri!”。
4. 运行与打包:
# 开发运行 (会同时启动前端开发服务器和 Tauri 窗口) npm run tauri dev # 构建应用 (生成在 src-tauri/target/release 或相应平台目录) npm run tauri build首次运行tauri dev会下载 Rust 依赖并编译,可能需要几分钟。
3.3 Electro Bun 实现
注意:Electro Bun 的 API 和项目结构在快速迭代中,以下示例基于其当前稳定模式。
1. 创建项目:
mkdir electrobun-hello && cd electrobun-hello bun init -y编辑package.json,添加依赖和脚本:
{ "name": "electrobun-hello", "module": "index.ts", "type": "module", "scripts": { "dev": "bun run --hot src/index.ts", "build": "bun build ./src/index.ts --outdir ./dist --target=bun" }, "devDependencies": { "electrobun": "latest" } }然后安装依赖:
bun install2. 项目结构:
electrobun-hello/ ├── package.json ├── bun.lockb ├── src/ │ └── index.ts # 主入口文件 └── index.html // 前端页面3. 编写代码:
src/index.ts- 主进程逻辑(Electro Bun 中前后端更一体化)。
import { Application, Window } from 'electrobun'; import { join } from 'path'; import { readFileSync } from 'fs'; const app = new Application({ window: new Window({ title: 'Electro Bun Hello', width: 800, height: 600, // 加载本地 HTML 文件 loadFile: join(import.meta.dir, '..', 'index.html'), }), // 定义 IPC 处理器 ipc: { async 'get-message'() { return `Hello from Electro Bun Backend! Time: ${new Date().toLocaleTimeString()}`; }, }, }); // 启动应用 app.start();index.html- 前端页面。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Electro Bun Hello</title> <script type="module"> // Electro Bun 通过全局的 `electrobun` 对象暴露 IPC document.getElementById('msgBtn').addEventListener('click', async () => { const message = await electrobun.ipc.invoke('get-message'); document.getElementById('message').textContent = message; }); </script> </head> <body> <h1>Hello from Electro Bun!</h1> <button id="msgBtn">Get Message from Backend</button> <p id="message"></p> </body> </html>4. 运行与打包:
# 开发运行 bun run dev # 构建 (注意:Electro Bun 的完整生产打包可能仍在演进中,请查阅其最新文档) bun run build4. 实测对比:数据说话
现在,三个应用都完成了。我们在同一台机器(2021款 M1 MacBook Pro, 16GB RAM)上,以开发模式运行它们,并进行以下维度的测量和对比。
4.1 安装与首次运行体验
| 维度 | Electron | Tauri | Electro Bun |
|---|---|---|---|
| 项目创建速度 | 最快 (npm init+forge import) | 慢 (需要引导选择并安装 Rust 依赖) | 快 (bun init) |
| 依赖安装速度 | 快 (npm/yarn) | 首次极慢 (需下载 Rust 工具链和编译) | 快 (Bun 安装极快) |
首次dev启动 | 快 | 非常慢 (需编译 Rust 代码) | 快 |
| 热重载 | 支持良好 (配合前端框架) | 支持良好 (前端热重载,Rust 部分需重新编译) | 支持 (--hot标志) |
| 上手难度 | 最低,纯 Web 技术栈 | 较高,需理解 Rust 项目结构、Cargo.toml、命令注解 | 中等,需适应 Bun 的 TypeScript 优先和 Electro Bun 特定 API |
小结:Electron 在“快速开始”上依然无敌。Tauri 的“慢”集中在首次 Rust 编译,后续开发尚可。Electro Bun 体验流畅,但你需要适应 Bun 的生态。
4.2 打包体积与内存占用(Release 版本)
我们使用各框架的默认配置进行生产构建。
| 维度 | Electron | Tauri | Electro Bun |
|---|---|---|---|
| macOS .app 体积 | ~120 MB | ~8 MB | ~25 MB (依赖 Bun 运行时) |
| 内存占用 (空闲) | ~120 MB | ~45 MB | ~80 MB |
| 内存占用 (点击交互后) | ~125 MB | ~48 MB | ~85 MB |
数据解读:
- Electron:体积和内存的“代价”直观可见。120MB 的安装包和内存占用是其架构决定的。
- Tauri:优势巨大。8MB 的安装包和最低的内存占用,对于需要分发或资源敏感的应用是决定性优势。
- Electro Bun:介于两者之间。比 Electron 轻量,但因为它仍打包了 Bun 运行时和 WebKit,所以比 Tauri 重。
4.3 启动速度与跨平台一致性
| 维度 | Electron | Tauri | Electro Bun |
|---|---|---|---|
| 冷启动时间 | ~800ms | ~400ms | ~600ms |
| 跨平台行为一致性 | 最高(Chromium 保证) | 高(依赖系统 WebView,Win/macOS 极好,Linux 略有差异) | 待观察(较新,社区反馈少) |
| 系统集成 | 需额外配置或插件 | 较好 (通过 Rust 直接调用系统 API) | 依赖 Bun 的 FFI 和框架封装 |
小结:Tauri 在启动速度上领先,这得益于本地二进制和系统 WebView。Electron 的“一致性”是双刃剑:带来了确定性,也带来了臃肿。Electro Bun 的表现令人期待,但需要更多时间验证。
4.4 开发体验与生态
| 维度 | Electron | Tauri | Electro Bun |
|---|---|---|---|
| 前端框架支持 | 任意 Web 框架 (React, Vue, Svelte, Solid...) | 任意 Web 框架 | 任意 Web 框架 (但与 Bun 工具链集成最佳) |
| Node.js 生态 | 完全访问 | 无法直接访问,需通过 Rust 重写或 FFI | 部分访问(通过 Bun,兼容大部分 Node API) |
| 系统 API 访问 | 通过electronAPI 或 Node 原生模块 | 通过tauriAPI (Rust 后端) 或插件 | 通过electrobunAPI 或bun:ffi |
| 调试工具 | Chrome DevTools (完整强大) | 系统 WebView 开发者工具 (功能因平台而异) | 依赖 Bun 的调试能力 |
| 打包配置复杂度 | 中等 (Webpack/Forge/Vite 等) | 简单 (tauri.conf.json) | 简单 (Bun 内置构建) |
5. 常见问题与排查思路
在实际开发中,你肯定会遇到各种问题。这里列出每个框架最典型的“坑”。
5.1 Electron 常见问题
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 应用启动白屏/崩溃 | 主进程代码错误,或 preload 脚本报错 | 查看终端或系统控制台日志 | 检查main.js语法,确保loadFile路径正确。逐步注释代码定位。 |
require is not defined | 渲染进程未启用nodeIntegration或上下文隔离配置错误 | 检查webPreferences配置 | 推荐模式:contextIsolation: true并使用preload脚本安全暴露 API。 |
| 打包后体积巨大 | 包含了未使用的依赖或未优化 | 分析node_modules | 使用electron-builder的asar和压缩选项。检查依赖,移除 devDependencies。 |
| GPU Process Launch Failed | 显卡驱动兼容性或 Chromium 沙箱问题 | 启动时添加--disable-gpu-sandbox参数测试 | 在app.commandLine.appendSwitch中添加相关 flags,但需权衡安全。 |
| 长时间播放视频卡死 | Chromium 媒体进程问题 | 检查系统资源占用 | 尝试更新 Electron 版本,或使用--disable-features禁用部分硬件加速特性。 |
5.2 Tauri 常见问题
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
tauri dev编译 Rust 极慢 | 首次编译或依赖更新 | 观察cargo build输出 | 使用国内镜像源。确保 Rust 工具链最新 (rustup update)。 |
| 前端无法调用 Rust 命令 | 命令未正确定义或注册 | 1. 检查 Rust 函数是否有#[tauri::command]。2. 检查 main.rs中是否invoke_handler注册。3. 检查前端 invoke函数名是否匹配。 | 仔细对照官方示例。使用tauri::generate_handler!宏。 |
| 打包失败,缺少 WebView2 | Windows 目标系统未安装 WebView2 | 查看错误信息 | 确保目标 Windows 10/11 系统已安装 WebView2 运行时。Tauri 打包时可选择嵌入运行时(增大体积)。 |
| Linux 上样式或字体异常 | 系统 WebKitGTK 版本或字体配置问题 | 在目标 Linux 发行版上测试 | 在tauri.conf.json中指定更兼容的webkit2gtk版本,或打包时包含字体。 |
fs等 Node API 无法使用 | Tauri 前端上下文无 Node.js | 理解 Tauri 架构 | 所有文件操作需通过 Rust 后端命令进行。使用tauri-plugin-fs等官方插件。 |
5.3 Electro Bun 常见问题
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
bun install失败 | 网络问题或包兼容性 | 检查 Bun 版本和网络 | 使用bun install --verbose。考虑使用国内镜像或检查包是否支持 Bun。 |
electrobun模块找不到 | 依赖未正确安装或版本问题 | bun pm ls electrobun | 确保使用最新 Bun 版本,并重新安装bun add electrobun。 |
| 窗口不显示或 IPC 不工作 | API 使用方式变更或 Bug | 查阅 Electro Bun 最新文档和 GitHub Issues | 框架较新,API 可能变动。以官方示例为准,并关注其 Discord 或 GitHub 动态。 |
| 打包后的应用无法运行 | 运行时依赖缺失 | 检查 Bun 运行时是否被正确打包 | 目前 Electro Bun 的完整生产部署方案可能仍在完善中,需仔细阅读其构建文档。 |
6. 最佳实践与选型决策指南
经过实测和对比,我们可以得出一些更落地的建议。
6.1 什么时候选 Electron?
- 团队核心技能是 Web 技术栈:不想学习新语言,希望全员快速上手。
- 项目需要快速原型验证 (MVP):时间紧迫,需要利用海量 npm 生态快速堆功能。
- 应用复杂度高,需要深度调试:Chrome DevTools 提供的调试能力无可替代。
- 需要访问大量特定的 Node.js 原生模块或 npm 包。
- 对安装包体积和内存占用不敏感:比如面向企业内部的工具,用户机器性能充足。
最佳实践:
- 始终启用
contextIsolation,并使用preload脚本进行安全通信。 - 使用
electron-builder或electron-forge进行打包,并精细配置files和asar以减小体积。 - 对于生产环境,考虑使用
electron-updater实现自动更新。
6.2 什么时候选 Tauri?
- 对应用体积和内存有极致要求:需要分发给海量用户,或运行在资源受限的设备上。
- 追求原生般的启动速度和运行性能。
- 看重应用的安全性:Rust 的内存安全特性是天然优势。
- 团队有 Rust 学习意愿或已有 Rust 经验。
- 应用功能相对独立,不重度依赖特定 Node.js 生态。
最佳实践:
- 首次搭建环境时,耐心完成 Rust 工具链的安装和编译。
- 前端使用 Vite 等现代构建工具,获得最佳开发体验。
- 充分利用
tauri.conf.json进行丰富的窗口、权限、打包配置。 - 系统级操作,优先寻找或开发 Tauri 插件,而非尝试桥接 Node 模块。
6.3 什么时候考虑 Electro Bun?
- 你是 Bun 和 JavaScript 全栈的早期采用者,喜欢其工具链的统一和速度。
- 项目是全新的,愿意尝试前沿技术,并能承受一定的 API 变动风险。
- 希望获得比 Electron 更轻量的体验,但又不想离开 JavaScript/TypeScript 生态。
- 应用不需要访问非常冷门的系统 API。
最佳实践:
- 紧密关注其官方文档和 GitHub 发布页。
- 在项目初期就锁定一个相对稳定的版本。
- 社区资源较少,遇到问题需要更主动地查阅源码和提交 Issue。
6.4 一个简单的决策流程图
开始 │ ├─ 你的团队是否精通或愿意学习 Rust? │ ├─ 是 → 强烈建议 Tauri │ └─ 否 │ ├─ 项目是否对安装包体积和内存极度敏感? │ │ ├─ 是 → 慎重评估,可能需要学习 Rust 选择 Tauri,或接受 Electro Bun 的早期风险。 │ │ └─ 否 → 继续 │ └─ 你是否追求最稳定、生态最丰富的方案? │ ├─ 是 → 选择 Electron │ └─ 否 → 可以尝试 Electro Bun (评估其当前成熟度是否满足需求)7. 总结
回到最初的问题:Electron 还有必要吗?
答案是:对于绝大多数需要“快速将 Web 能力扩展到桌面”的场景,Electron 仍然是目前最稳妥、风险最低的选择。它的庞大体积,换来的是无与伦比的开发效率、生态确定性和团队兼容性。VSCode、Figma(桌面端)、Notion(桌面端)等顶级应用的存在,证明了其能力上限足以支撑最复杂的应用。
但是,“必要”不等于“唯一”。Tauri 在性能、体积和安全性上的优势是实实在在的,它代表了桌面应用框架的一个重要发展方向。如果你的团队有能力跨越 Rust 的初始门槛,它带来的收益将是长期的。而 Electro Bun 则为我们展示了另一种可能性:在 JavaScript 生态内寻求更优解。
技术选型没有银弹。本文提供的实测数据、问题清单和决策指南,希望能帮助你跳出“技术辩论”,回归到项目需求、团队能力和交付目标的本质,做出最适合自己的那个选择。
建议你将本文收藏,在启动下一个桌面项目时,再拿出来对照一遍。毕竟,选对起跑线,比赛就赢了一半。