还在用 Electron?6 种跨平台桌面方案横评:Rust + Vue 把安装包从 224MB 干到 4.7MB
几个月前在交付一个桌面工具时,我对着打包机出来的 exe 发呆——安装包 224MB,解压后超过 500MB,用户下载时吐槽"这工具比大型游戏还大"。Electron 确实把跨平台桌面开发的门槛降到了"会写网页就能做",但代价是直接把一个 Chromium 塞进每个用户的硬盘里。那段时间我翻遍了全网的跨平台桌面方案,最后用 Rust + Vue 通过 Tauri 把同样的功能做到了 4.7MB 的安装包,启动速度还反超了。这篇横评不是哪个框架的官方软文,而是我踩完坑之后,把 6 种主流方案的体积、性能、开发体验和适用场景放在同一张桌子上做的对比。
我先说结论:如果你的产品是功能复杂的大中型应用,Electron 依然是"最不坏"的选择;但如果你做的是工具类软件、内部系统、对分发体积敏感的商业软件,或者想让产品在老旧电脑上也跑得动,Tauri 这类"系统 WebView + 编译型后端"的方案已经把优势拉到了肉眼可见的级别。下文会把这 6 种方案挨个拆开,重点讲清楚 RUST + VUE 这条路是如何把安装包压到 4.7MB 的,以及你在迁移时会遇到的真实问题。
1. 224MB 的困境:Electron 的"巨人病"从哪来
1.1 体积膨胀的根源:一个应用,绑了一个浏览器内核
先打破一个直觉:Electron 安装包 224MB 并不是因为它打包了"你的代码"——你的代码可能只占不到 5MB。真正撑爆体积的是两样固定零件:Chromium 渲染引擎和 Node.js 运行时。
Chromium 有多胖?它在 Electron 里负责渲染所有界面,包括 HTML、CSS、JavaScript,还要处理 Canvas、WebGL、视频解码、网络请求、GPU 加速等一堆底层能力。Node.js 则提供文件读写、子进程、操作系统交互的接口。Electron 的"跨平台"本质上是"把整个浏览器的复杂度和容量都复制一份给每个用户"。
我做过一个控制台式的数据管理工具,纯前端逻辑加几个 npm 依赖,源码打包出来 2.8MB,塞进 Electron 之后安装包变成了 180MB 起步。这还只是直出包,如果你用了 electron-builder 的默认配置,没做任何裁剪,体积很容易突破 200MB。用户下载后解压安装,占用的磁盘空间通常又是安装包的 2 到 3 倍,因为安装器会做解压、移动、注册等操作。
1.2 不止是体积:下载、启动、内存三笔隐藏账单
很多团队只关注安装包大,但实际付账的是另外三样:
- 下载转化率:国内很多用户在低带宽网络环境下,看到 200MB 的安装包会直接放弃。即使有镜像站,多 100MB 就意味着多几十秒等待,对一个功能只有几 MB 的小工具来说,这是致命的转化门槛。
- 启动时间:Electron 应用启动时要初始化 Chromium、加载 V8 引擎、解析主进程脚本、再建立渲染进程,三步走完,空窗口也要 2 到 4 秒。在机械硬盘的老电脑上,这个数字可以膨胀到 8 秒以上。
- 内存占用:一个最简单的 Electron 空应用,内存占用轻松跑进 150MB—200MB。用户可能同时开了浏览器和办公软件,再加一个动辄几百 MB 的 Electron 应用,内存直接告急。
不是说 Electron 一无是处,它拥有最成熟的生态、最完整的开发者工具、Electron 菜单系统、app.getAppMetrics 这类系统级 API 也很完善,遇到问题几乎都能搜到答案。但它解决的是"快速做出桌面应用"的问题,而不是"做一个轻量好用的桌面应用"的问题。这篇横评的出发点很简单:如果你的项目对体积敏感,或者想让应用在更广泛的用户设备上流畅运行,你还有别的路可走。
2. 六条路线的全景对比:不是所有"轻量"都轻得一样
2.1 横评方案与测试基准
先交代清楚这次横评的范围。我选了 6 种个人团队和中小公司最常用的跨平台桌面方案做横向对比:
| 方案 | 后端/核心语言 | 前端技术 | 安装包体积参考(实测) | 空应用内存占用参考 | 学习曲线 |
|---|---|---|---|---|---|
| Electron | Node.js/JavaScript | HTML/CSS/JS 任意框架 | 180MB 起 | 150MB 起 | 低(Web 开发者友好) |
| Tauri | Rust | 系统 WebView + 任意前端框架 | 3MB 起 | 30MB 左右 | 中高(需学 Rust) |
| Wails | Go | 系统 WebView + 任意前端框架 | 5MB 起 | 30MB 左右 | 中(需学 Go) |
| Flutter Desktop | Dart | 自绘引擎,Dart Widget | 40MB 起 | 80MB 左右 | 中(移动端迁移有优势) |
| PySide6 / PyQt | Python + Qt | QtWidgets 或 QML | 60MB 起 | 40MB 左右 | 低(Python 开发者友好) |
| Avalonia / .NET | C# / .NET | XAML | 50MB 起 | 60MB 左右 | 中(.NET 团队友好) |
测试基准我设定得很朴素:同一个"登录页 + 数据表格 + 一个文件下载功能"的简单应用,在 Windows 10 x64 上分别用 6 种方案实现,统一用 release 模式打包,不加载任何第三方 UI 框架,插件只保留必要部分。这么做的目的是让体积和性能数据有可比性,而不是拿一个 10 个插件的大项目去比一个空壳。
2.2 为什么体积差距能到几十倍:捆绑与借用的区别
看完上表你可能已经注意到,Tauri 和 Wails 的体积几乎可以用"离谱"来形容。它们的核心思路是一样的:不内置浏览器引擎,而是调用操作系统自带的 WebView 组件。
Windows 从 Win10 开始系统级内置了 WebView2(基于 Chromium 内核,但由系统维护,版本统一),macOS 有 WKWebView,Linux 发行版普遍自带 WebKitGTK。Tauri 和 Wails 只是把你在前端写的 HTML/CSS/JavaScript 交个系统 WebView 去渲染,后端则编译成一个独立的原生二进制文件(Rust 或 Go),负责窗口管理、文件系统、网络请求、系统调用等真正需要原生能力的部分。
这就像你出远门不再需要把整套厨房设备搬进车里,而是到了目的地直接用当地共享厨房——锅碗瓢盆有人帮你维护,你只需要带上食材和菜谱。
Flutter 走的是另一条路:它自绘所有 UI 组件,不依赖系统 WebView,也不依赖操作系统的原生控件树。好处是所有平台渲染效果完全一致,坏处是自绘引擎本身就有挺大的固定体积,40MB 起跳,比 Tauri 大一个量级,但也远小于 Electron。
PySide6 和 Avalonia 则各自站在 Python 和 .NET 生态的肩膀上,体积介于两者之间,核心优势更偏向"开发效率"和"生态继承",而不是"体积最小化"。
2.3 选型先回答三个问题,再谈框架
在做具体对比之前,我建议你先回答自己三个问题,答案会直接帮你砍掉一半选项:
- 你的团队主力语言是什么?全栈 JS 团队选 Electron 或 Tauri 都行;Python 算法团队选 PySide6;Go 后端团队选 Wails;以 Windows 为中心的 .NET 团队可以看看 Avalonia。
- 你的用户是谁?如果是内部工具、极客用户,WebView2 依赖不算事;如果面向小众系统环境或老旧 Windows 7 用户,Tauri 可能会因为 WebView 缺失让你头疼。
- 你愿不愿意为一个更小的安装包投入额外学习成本?Rust 的学习曲线是真实存在的,如果团队没有任何 Rust 经验,前两周开发速度会明显下降。这不是恐吓,是提前让你有心理预期。
3. 4.7MB 究竟怎么做到的:Tauri + Vue 实测拆解
3.1 架构优势:系统 WebView + Rust 二进制
现在进入本文重点:把安装包做到 4.7MB 的 Tauri 到底是什么来头。
Tauri 是当前用户量增长最快的跨平台桌面框架之一,核心由两部分组成:Rust 编写的后端(Tauri Core)和任意前端框架(Vue、React、Svelte 都可以)。它的运行模型比 Electron 更"分工明确":
- 前端层(Vue):负责所有界面渲染和用户交互,代码会被编译成静态资源,运行时交给系统 WebView 展示。
- 后端层(Rust):负责窗口生命周期、系统托盘、文件系统、网络请求、命令行参数等原生能力。前端要调用原生能力时,通过 Tauri 自带的 IPC 桥(invoke 机制)发送指令,Rust 侧执行完再把结果返回前端。
这就是为什么安装包能这么小:前端静态资源总共不过几百 KB;Rust 编译出的原生二进制对特定 CPU 架构的 Windows 来说也就 2MB 到 3MB,两者相加,加上安装器基础设施,最终 4.7MB 就成了一个非常自然的结果。
3.2 从零搭建一个 Tauri + Vue 项目的完整流程
直接给你我实测过的操作路径,照着走基本一次就能跑通。
第一步,环境准备。你需要安装:
- Rust 工具链:从 rustup.rs 下载安装,安装完成后运行
rustc --version确认,Tauri 需要 Rust 1.60+ 版本,我用的是 1.77 稳定版,后面没有遇到兼容性问题。 - Node.js 16 及以上版本:用 nvm 管理最方便,避免权限问题。
- Windows 平台需要 WebView2 Runtime。Win11 出厂自带,Win10 多数也已有;如果目标用户系统太旧,可以从微软官网下载 WebView2 安装引导包,体积只有 1MB 左右,不算负担。
第二步,创建 Vue 项目。工具链我建议直接用npm create vue@latest脚手架,它是官方维护的,生成的项目结构清晰,TS 默认支持也到位。执行后按照交互提示选择需要的功能,默认配置即可。
npm create vue@latest my-tauri-app cd my-tauri-app npm install第三步,安装并初始化 Tauri CLI。
npm install -D @tauri-apps/cli npm run tauri inittauri init会问你一些配置:窗口名称、窗口标题、前端 dev 服务器地址、构建命令等。这里有个容易踩的坑:必须保证 tauri.conf.json 里的build.beforeDevCommand和devUrl与你的 Vue 项目实际启动命令匹配。Vite 项目默认 dev 服务器地址是http://localhost:5173,端口的改动要同步更新到配置文件。
第四步,修改src-tauri/tauri.conf.json的关键配置项。这是我用来把体积压到 4.7MB 的核心之一:
{ "build": { "beforeDevCommand": "npm run dev", "beforeBuildCommand": "npm run build", "devUrl": "http://localhost:5173", "frontendDist": "../dist" }, "app": { "windows": [ { "title": "My Tauri App", "width": 1024, "height": 768 } ], "security": { "csp": null } }, "bundle": { "active": true, "targets": ["nsis"], "icon": [ "icons/32x32.png", "icons/128x128.png", "icons/icon.ico" ] } }第五步,安装 Vue 和 JS 侧 API。
npm install @tauri-apps/api第六步,开发模式联调。运行npm run tauri dev,Tauri 会先启动 Vite dev server,然后编译 Rust 后端并打开一个窗口。第一次编译会比较慢,因为要拉取和编译大量 Rust 依赖,等 2 到 5 分钟很正常,之后有了缓存就快了。
第七步,打包发布版本。运行:
npm run tauri build这个过程会先执行npm run build打包 Vue 静态资源,再编译 Rust release 二进制,最后用 NSIS 打包成 Windows 安装程序。我这边最终产出的 exe 安装包刚好 4.7MB。
3.3 代码实操:前端如何调用 Rust 后端能力
Tauri 的前后端通信和 Electron 的 IPC 类似,都是异步消息模式。区别在于 Tauri 的 invoke 机制用起来更像一个本地 RPC。
在 Rust 侧(src-tauri/src/lib.rs)定义一个相对简单的函数,比如读取文件内容:
use tauri::command; #[tauri::command] fn read_config_file(path: String) -> Result<String, String> { std::fs::read_to_string(&path).map_err(|e| e.to_string()) } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![read_config_file]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }在 Vue 组件里调用:
import { invoke } from '@tauri-apps/api/core'; import { ref } from 'vue'; const fileContent = ref(''); async function readFile() { try { fileContent.value = await invoke<string>('read_config_file', { path: 'C:\\Users\\Public\\config.ini' }); } catch (e) { console.error('读取失败:', e); } }注意参数名的映射规则:Tauri 的 invoke 默认会把 JS 侧参数名按 camelCase 转为 snake_case,所以我在 JS 里写path,在 Rust 里对应path;如果你写filePath,Rust 侧参数名就要写成file_path,否则会报"命令不匹配"的错误。这个细节最容易被忽略。
3.4 体积压缩的关键动作清单
回到项目开头,我不是把 Tauri 创建完就自动变成 4.7MB 的。如果你新拉一个默认模板直接打包,通常会得到 8MB 到 12MB 左右的安装包。以下三个动作是我实测中体积降幅最明显的:
- 裁剪图标资源:默认脚手架会生成 10 多个不同尺寸的 PNG 和 ICO 图标,累计能占到 1MB 多。我只保留
icon.ico(用于 Windows 安装器和窗口图标)和 128x128 的 PNG(用于应用图标),删除其余文件后,安装包直接减少近 1MB。 - 开启 Rust release 优化:在
Cargo.toml的[profile.release]段加上lto = true、codegen-units = 1、opt-level = "s"(优先减小二进制体积)。这三个配置能显著减小 Rust 二进制体积,代价是编译时间变长,但你的安装包能小 500KB 到 1MB。 - 按需启用 Tauri 特性:默认
Cargo.toml里的tauri依赖带了完整特性集,但实际项目可能用不到窗口阴影、DevTools 等。可以删掉不需要的 features,只保留["tray-icon", "image-png"]这类必要项,能砍掉一部分编译产物。
做完这三步之后,你还可以用 UPX 对 exe 做可选压缩,但 UPX 会拖慢启动速度,而且有时会被杀毒软件误报,我建议生产环境不要开,开发环境玩玩就行。
4. 另外四种方案的真实手感:对标完体积,还得对标日常
4.1 Wails:Go 后端的"平替版 Tauri"
Wails 和 Tauri 的思路几乎同源,都是调系统 WebView,前端框架随便用(Vue、React、Svelte 都支持)。区别在于后端用 Go,而不是 Rust。对 Go 生态熟、但对 Rust 有心理负担的团队,Wails 是一个很顺手的过渡选项。
我实测 Wails v2 打包出的安装包在 6MB 到 8MB 之间,比 Tauri 大一些,但依然远超 Electron 的级别。Go 的运行时模型简单,交叉编译方便,如果你想让后端逻辑和现有 Go 微服务共用代码库,Wails 的诱惑会非常大。
但它有几个弱点我需要提醒:
- WebView2 依赖仍然存在。Windows 上如果目标系统没有 WebView2,你需要引导用户安装,这会让"实际下载体积"从 6MB 变成 7MB 左右,还不算额外步骤带来的流失。
- Tauri 的插件生态更丰富:Tauri 社区已经有比较成熟的自动更新、系统托盘、窗口阴影、全局快捷键等插件;Wails 很多功能需要自己造轮子。
- Go 二进制本身比 Rust 大:Go 是静态链接、自带运行时和 GC,同样逻辑编译出的二进制通常比 Rust 大 20% 到 50%,所以走到极致体积优化时 Wails 很难做到 Tauri 那么小。
4.2 Flutter Desktop:自绘引擎是把双刃剑
Flutter Desktop 用 Dart 语言加自绘引擎,不依赖系统 WebView。它最大的优点是"写一套代码,移动端和桌面端渲染完全一致"——不会出现同一套 Vue 在 Chrome 里正常、在 WebView2 里却样式错位的问题。安装包体积实测在 40MB 到 60MB 之间,远小于 Electron,但和 Tauri/Wails 不在一个数量级。
实际开发中的体验是:Flutter 的热重载效率高、UI 组件丰富,适合做复杂交互界面;但桌面端的输入体验(中文输入法、键盘快捷键、鼠标右键菜单)在部分系统上仍有小问题,有些需要写平台通道调用原生代码。
如果你已经在用 Flutter 做移动端 App,顺手做桌面版是划算的选择;如果是从零开始只为做一个桌面工具,我不建议为了 Flutter 去学 Dart,Web 前端技术栈的人员冗余度会更友好。
4.3 PySide6 / PyQt:Python 项目的桌面出口
PySide6 和 PyQt 是 Python 绑定 Qt 框架的两条主流路线。它们的优势很明显:Python 上手快、生态强(科学计算、数据分析、AI 模型推理都能直接接进来)。如果你要做的工具本身就是数据清洗、模型调试这类以 Python 为核心的工作流,用 PySide6 不需要引入第二门语言。
但体积是它的软肋。用 PyInstaller 打包一个 PySide6 应用,即使做了 UPX 压缩,安装包也常在 60MB 到 150MB 之间。这个体积不是 Qt 本身多大,而是 Python 解释器、Qt 动态库、各种依赖库的集合。在横评中它排名第四——能用,但不够极致。
还有两个隐形成本:PySide6 是 LGPL 协议,如果你动态链接就不会传染;但如果用户要修改 Qt 本身的行为,需要提供再授权。商业闭源项目建议仔细读一遍许可证,PyQt 则是 GPL 或商业授权,选型时别只看功能。
我的建议是:Python 团队做内部分发工具可以直接用 PySide6,效率第一;但如果你做的是对外分发的商业软件、又特别在意用户下载体验,PySide6 在体积上的劣势会影响你的决策。
4.4 Avalonia / .NET MAUI:.NET 系的跨平台路线
Avalonia 是 .NET 生态里最成熟的跨平台 UI 框架,使用 XAML 布局,和 WPF 的开发模式非常接近。对于有 WPF/WinForms 经验的团队,转 Avalonia 的学习成本极低。它不依赖系统 WebView,而是自绘所有控件,因此渲染一致性较好。
实测 Avalonia 应用的安装包体积在 50MB 到 80MB 之间,.NET NativeAOT 技术可以把体积进一步压缩,但配置复杂度会上升很多。这里我不展开细节,你只需要了解:.NET 团队如果已经重度使用 C#,选 Avalonia 是低成本、中等体量的平衡方案,但它做不到 Tauri 那样的极限体积。
4.5 六种方案的发包策略横向比较
除了体积,跨平台应用还有几个和体积同等重要的关键维度:
| 方案 | 跨平台表现 | 自动更新难易 | 原生能力扩展 | 社区与文档成熟度 |
|---|---|---|---|---|
| Electron | 极强 | 成熟(electron-updater 等) | 极强(npm 生态 + Node 原生模块) | 极高 |
| Tauri | 较强(受系统 WebView 版本影响) | 有插件但需自己搭 | 强(Rust crate 丰富) | 中高,增长快 |
| Wails | 较强 | 需自己实现 | 强(Go 生态) | 中 |
| Flutter Desktop | 较弱(桌面端适配还在推进) | 一般 | 弱(需写平台通道) | 中 |
| PySide6 | 极强 | 弱 | 强(Python/C++ 都能调) | 高 |
| Avalonia | 较强 | 一般 | 中等 | 中高 |
这轮比较可以看作是"选型第二阶段":确定体积满足要求后,你要考虑的是长期维护成本。Electron 的优势在于你几乎不会遇到"不知道找谁"的问题;Tauri 社区这两年成长非常快,遇到问题在 GitHub Discussions 里通常能快速得到反应,但文档的某些边角不够细致,尤其是 Linux 平台依赖问题。
5. 从 Electron 搬到 Tauri:我的迁移实录和踩坑清单
5.1 迁移前的盘点:哪些能留下,哪些必须重写
迁移不是推倒重来。以我那个 224MB 的工具为例,它的技术栈是 Electron + Vue + TypeScript + 一组 npm 插件。迁移到 Tauri 后,让我感到庆幸的是:渲染进程的 Vue 代码几乎原封不动,组件、状态管理、页面路由都不用改。真正要动的是主进程(main process)里所有调用 Node API 的逻辑。
你首先要做一次功能盘点,把主进程代码里涉及的 Node 模块列出来:fs、child_process、http、electron-store、electron-updater、node-notifier、sharp等。然后逐一确认 Tauri 这边有没有对应方案:
- 文件系统读写:Tauri 用
tauri::plugin-fs或直接用 Rust 标准库std::fs写命令,前者更省事。 - 网络请求:Vue 前端依然可以用
axios/fetch,但 CORS 限制在 Tauri 里可以通过 CSP 配置放宽;如果需要绕过 CORS 或者做更底层的 HTTP 请求,建议在 Rust 侧用reqwest写命令。 - 托盘、菜单、通知:Tauri 官方有
tray-icon、notification插件,API 风格和 Electron 不同,但功能基本覆盖。 - 自动更新:Tauri 官方有
updater插件,但需要自建更新服务器或兼容的静态服务器,比 electron-updater 稍微麻烦一点。 - 子进程调用:原本用
child_process.spawn的逻辑,在 Rust 侧可以用std::process::Command实现。注意 Rust 在 Windows 上创建子进程时,要设置CREATE_NO_WINDOW标志,避免弹出的黑框闪窗。
我遇到最大的坑在主进程里的数据库访问。原来的工具直接用better-sqlite3读写本地 SQLite 文件。Tauri 生态里比较成熟的方案是tauri-plugin-sql,它对 SQLite 的封装做得不错,但这意味着你要把原有 SQL 语句迁移到插件 API 上,如果原来的 SQL 用了大量自定义函数或复杂事务,迁移成本会肉眼可见地上升。
5.2 从 Node 到 Rust:主进程重写的真实感受
Rust 和 Node 的编程体验差异是明显的。Node 风格是"需求直达——fs.readFileSync 一行完事",Rust 风格是"先把类型和错误想好,再写逻辑"。
拿文件读取来说,Node 版本:
const fs = require('fs'); try { const data = fs.readFileSync('/path/to/file', 'utf8'); return data; } catch (e) { console.error(e); }Rust 版本的 Tauri 命令:
#[tauri::command] fn read_text_file(path: String) -> Result<String, String> { std::fs::read_to_string(&path) .map_err(|e| format!("Failed to read {}: {}", path, e)) }差别看起来不大,但 Rust 对错误处理是强制要求:你必须显式返回Result,没有"抛异常捉住就完事"这种宽容。第一次从 JS 迁移过来的同学会很不习惯,但好处是:上线后几乎不会出现"某个路径读不到文件然后静默失败"的问题。
另一个值得注意的点是字符串编码。Node 里fs.readFile默认按 UTF-8 处理,Rust 的read_to_string也一样,但遇到 GBK 编码的 Windows 配置文件会直接报错。处理旧系统文件时,建议加上encoding_rscrate 做编码探测和转换。这些细节不上一次线你是不会意识到的。
5.3 构建链路与 CI 的适配
迁移到一个新框架,CI/CD 基础设施也要跟着换。Electron 项目在 CI 上通常直接跑electron-builder --win --linux --mac;Tauri 则需要反过来解决一个更"原汁原味"的问题——每次 CI 跑npm run tauri build都要编译一遍 Rust crate。
为了不让 CI 每次花 10 多分钟编译依赖,我做了三件事:
- 在 CI 脚本里缓存
$CARGO_HOME目录(包括registry和git),这样第二次开始就不需要重新拉取 crate。 - 把
src-tauri/target目录也缓存起来,release 模式的增量编译会让后续构建快很多。 - 在
Cargo.lock锁定依赖版本,避免依赖变动导致缓存失效和构建不可复现。
Tauri 在 CI 上还有一个让我头疼的点:Linux 构建需要系统安装一堆 WebKitGTK 相关依赖(libwebkit2gtk-4.0-dev、build-essential、libssl-dev 等)。不同发行版的依赖名还略有差异。建议直接参考 Tauri 官方文档的 GitHub Actions 模板,它已经把常见发行版的依赖安装命令整理好了,直接用能省不少时间。
5.4 迁移后的实测:启动速度和内存的数据对比
迁完之后我连夜跑了一轮性能测试。同一台 Windows 11 测试机上,同样打开登录页、加载 1000 行数据表格、触发一次文件下载,数据如下:
| 指标 | Electron 版本 | Tauri 版本 |
|---|---|---|
| 安装包体积 | 224MB | 4.7MB |
| 首次启动到窗口可见 | 约 2.8 秒 | 约 0.9 秒 |
| 冷启动后内存占用 | 约 210MB | 约 38MB |
| 打包构建时长(首次) | 约 3 分钟 | 约 8 分钟(含 Rust 首次编译) |
| 打包构建时长(缓存后) | 约 1.5 分钟 | 约 3 分钟 |
启动速度提升 3 倍,内存占用降到原来的五分之一,这个数据对硬件条件差的用户来说是非常直观的体感提升。但也要诚实:Tauri 的首次编译速度慢得多,如果你还要改 Rust 代码,每次tauri dev的增量编译也需要等待(通常是 5 到 15 秒,取决于改动范围),这确实会影响开发节奏。我的解决办法是尽量把与界面相关的逻辑留在前端做,Rust 侧只保留真正需要原生能力的命令,减少反复编译 Rust 的次数。
6. 关于 WebView2 和跨平台一致性的冷思考
6.1 系统 WebView 的版本碎片化问题
Tauri 方案的底层依赖是系统 WebView,这也是它最容易被质疑的地方。Windows 系统的 WebView2 Runtime 虽然是系统级组件,但它是由 Microsoft Edge 团队统一更新,理论上是"持久稳定"的。但现实是:你的用户可能在 Windows 10 老版本上运行一个被 IT 部门禁用了自动更新的 WebView2 Runtime,版本停留在好几个月前。
我在实际分发中遇到的一个案例:某企业用户的环境只允许安装白名单软件,WebView2 Runtime 版本过老,导致应用内一个依赖新版 WebView API 的组件渲染异常。倾查了三天才定位到原因。这类问题在 Electron 里几乎不存在,因为 Chromium 是跟着你的安装包走,版本受你控制。
应对策略就两条:一是尽量只用基础 Web API,避免使用太新的浏览器特性。Vue 3 编译出的代码通常会转译为 ES2015+,大部分特性在旧版 WebView2 上问题不大;二是做好版本检测,在启动时读取 WebView2 的版本号,对过低版本弹出引导升级的页面。
6.2 macOS 与 Linux 的差异:不是"一次编写,到处调试"
如果你要发布 mac 和 Linux 版本,一定要亲自在对应系统上跑一遍 UI,不要因为 Windows 上没问题就乐观。macOS 的 WKWebView 和 Windows 的 WebView2 在内核能力、CSS 渲染细节上都存在差异,常见问题有:
position: fixed在某些 WebKit 版本中配合transform会出现父级定位失效。- 字体渲染在 macOS 上默认使用系统字体平滑,行高和 Windows 不一致,可能导致弹窗溢出。
- 右键菜单和文件拖拽的行为在各个平台上的默认表现不同,Tauri 提供的事件钩子也各不相同,建议在开发初期就针对三平台做 UI 冒烟测试。
Linux 上还要注意发行版差异:Ubuntu 和 Fedora 的 WebKitGTK 版本不同,有的发行版需要额外安装libayatana-appindicator才能正常显示托盘图标。这些细枝末节虽然不影响 90% 用户,但你只要踩到一次,就会花掉半天时间。
6.3 什么时候真的不应该用 Tauri
权衡了这么多,我给出一个"反选"清单,帮助你也避免犯错:
- 你的应用重度依赖 Chrome 内核的高级特性,比如复杂的 WebGL 渲染、PWA 离线能力、Chrome 扩展系统。这类场景 Tauri 的 WebView2 在能力边界上可能会让你抓狂。
- 你的团队完全没有 Rust 经验,也没有人愿意学。硬上 Tauri 会让前两周的开发效率大幅下降,这不是框架的问题,是选型匹配的问题。
- 需要支持 Windows 7 / Windows 8 的存量用户。Tauri 官方声明支持 Windows 7,但 WebView2 在 Windows 7 上安装体验是比较麻烦的,建议直接用 Electron 更稳妥。
- 你的应用需要在无头环境(没有图形界面的服务器)中运行。Tauri 的窗口管理依赖 GUI 环境,纯后端守护进程场景下 Electron 主进程反而更灵活、更好编写。
我身边就有朋友做了反例:把公司内部一个包含大量复杂 Web 表格和旧版 Chromium 特性的系统硬迁到 Tauri,最终因为一个图表库在 WebView2 上的渲染问题被迫回退。迁移是有收益上限的,不是所有 Electron 项目都值得换。
7. 我的最终选型建议与团队落地路径
7.1 适合 Tauri + Vue 的典型画像
这轮横评做完,我给"什么项目适合 Tauri + Vue"画了一个比较清晰的轮廓:
- 工具类、效能类、效率类桌面应用:如笔记软件、截图工具、数据清洗工具、资源管理器替代品、开发者工具。这类应用逻辑相对独立,前端为主,原生能力只用来调用文件系统、系统命令、显示通知。
- 对安装包体积敏感的分发场景:通过官网直接下载、通过邮件发送安装包、在应用商店分发有大小限制等。
- 团队本身有 Vue/React 前端经验,且愿意投入一到两周学习 Rust 基础。你不需要成为 Rust 专家,只需要能写简单的
#[tauri::command]函数和调用常用的 crate 即可。 - 需要远程更新且希望更新包越小越好:Tauri 自动更新下载的就是应用本身的二进制和前端资源差量,更新包体积和安装包同级,对带宽敏感的场景优势明显。
7.2 不同团队的选择对照
我给不同背景的团队一个比较直给的选型建议,方便你做决策:
| 团队情况 | 推荐方案 | 核心理由 |
|---|---|---|
| 全栈 JS 团队,做内部工具,对体积无要求 | Electron | 生态最熟,开发效率最高 |
| 全栈 JS 团队,做对外工具,在意下载转化率 | Tauri | 体积小一个量级,前端代码基本复用 |
| Go 后端团队 | Wails | 学习成本低,可复用 Go 业务代码 |
| Python 团队做数据分析/算法工具 | PySide6 | Python 生态无缝衔接 |
| Flutter 移动端团队 | Flutter Desktop | 一端开发,多端复用 |
| .NET / WPF 团队 | Avalonia | 保留 C# 技术栈,桌面开发体验连贯 |
这张表的前提是"你从零开始选型"。如果你已经有一个跑得不错的 Electron 项目,迁移前务必做一次投资回报分析:预估迁移工时、收益和风险。确实有的人会告诉你"体积不重要,功能才重要",但当一个桌面工具安装包比功能体积大 40 倍时,你很难对用户解释清楚这笔体积账。我的做法是:新项目全部默认 Tauri,老项目只有在"体积成为真实痛点"时才启动迁移。
7.3 落地迁移的三条关键路径
如果你想从 Electron 迁移到 Tauri,不要梦想一步到位重写所有功能。我强烈建议分三步走:
第一步:技术验证(1 周)。不迁移任何真实功能,先建立 Tauri + Vue 的骨架,把你项目中最复杂、最依赖原生能力的一个功能(比如文件监听、数据库连接)用 Rust 实现一遍。如果这一步在一周内走通,说明这个项目适合迁移;如果一周后还卡在编译环境或 IPC 通信上,请停下来重新评估。
第二步:新功能全部走 Tauri,旧功能仍留在 Electron。意思别搞并行版本,而是把"新迭代"做到 Tauri 里,通过 Tauri 的 window 间通信能力调用旧应用的部分能力。这个阶段会比较痛苦,因为要维护两个技术栈,但风险可控。
第三步:灰度迁移核心模块。用户看到的是同一个应用,只是安装包从 224MB 变成几 MB。迁移一个模块、上线一个模块,不做大爆炸式重构。我最终就是这么做的,整个过程大约花了三周,其中大部分时间花在重写主进程的文件监听逻辑上。
8. 最后再分享几个 Tauri 日常开发的小技巧
如果读到这里,你已经决定要试试 Tauri,下面这几个小经验是我实际开发中摸索出来的,希望能帮你少浪费些时间:
做一个跨平台桌面方案,体积能压到多少,本质上取决于你在"捆绑"和"借用"之间做的取舍。Electron 选择捆绑,换来了平台一致性和巨大的生态;Tauri 选择借用,换来了极致的体积和启动速度,代价是要理解系统 WebView 的脾气,还要啃一点 Rust。没有哪个方案是绝对"最好"的,只有和你的团队、用户、业务目标最匹配的。拿我自己的项目来说,224MB 到 4.7MB 这个数字变化确实吸引了不少用户下载,但真正让我觉得迁移值回票价的,是启动速度从近 3 秒降到 1 秒以内——这个体感,用过 Electron 版再切 Tauri 版的人都懂。