前几天一个朋友找我诉苦:他们的 Electron + Vue 桌面客户端,安装包已经涨到 224MB 了,公司内网分发还好说,外网用户一看下载体积直接劝退。我说你换个思路试试,前端还是那套 Vue,把后端从 Node.js 换成 Rust,用 Tauri 那套方案重打包一次。他半信半疑地试了,结果安装包从 224MB 直接干到 4.7MB,启动速度还快了一大截。这篇文章就把这次实践完整复盘一下,顺带把市面上常见的 6 种跨平台桌面方案放在一起横向对比,讲讲体积差距背后的原理,以及 Tauri + Vue 从搭建到打包的完整实操流程。适合正在做桌面客户端选型、或者被 Electron 安装包体积折磨过的团队参考。
1. 六大跨平台桌面方案全景:先看这张对比表
桌面客户端开发这两年特别热闹,光是“跨平台”这个赛道,能打的方案一只手数不过来。我按技术栈和生态成熟度,筛出了最常被拿出来对比的 6 种:Electron、Tauri、Wails、Flutter Desktop、PySide6(Qt for Python)、Qt/C++ 原方案。它们的目标都是“一套代码,多端运行”,但底子完全不同,出来的产物差别也大得离谱。
1.1 方案阵容与选型背景
先说 Electron,严格来说它不是一门语言,而是一个运行时:把 Chromium 浏览器内核和 Node.js 打包在一起,前端工程师用 HTML/CSS/JS 写界面,Node.js 负责调用系统能力。这套组合在开发体验上几乎无敌,生态也最成熟,Slack、VS Code、Discord 都是它的代表作。代价就是安装包体积和内存占用一直被人吐槽。
Tauri 是这次横评的主角,它的思路是用 Rust 写后端,前端照样用 Web 技术(Vue、React 都行),但不再内置 Chromium,而是调用操作系统自带的 WebView 组件渲染界面。这个差异直接决定了两者在体积上的巨大鸿沟。
Wails 和 Tauri 思路类似,用 Go 写后端,同样复用系统 WebView,区别主要在语言生态和 Rust 与 Go 的各自优势上。
Flutter Desktop 是移动端跨平台框架 Flutter 的桌面延伸,用 Dart 语言和自绘 UI 引擎,不走 WebView,渲染性能很稳。
PySide6 是 Qt 官方钦定的 Python 绑定,适合 Python 技术栈的团队做工具类软件,但打包体积和分发体验一直是痛点。
Qt/C++ 则是传统原生 GUI 方案里的常青树,性能天花板最高,代价是开发效率相对低,对前端工程师尤其不友好。
1.2 核心指标对比表
我根据自己的实测和使用经验,把六个方案的关键指标整理成一张表,方便你直观感受差距:
| 方案 | 后端语言 | 界面技术 | 安装包体积量级 | 运行时内存(空应用) | 冷启动速度 | 生态成熟度 | 适合场景 |
|---|---|---|---|---|---|---|---|
| Electron | JavaScript/Node.js | React/Vue/任意 Web | 150MB - 250MB | 150MB - 300MB | 1.5s - 3s | 非常成熟 | Web 团队快速交付,复杂业务 |
| Tauri | Rust | Vue/React/任意 Web | 3MB - 15MB | 30MB - 80MB | 0.4s - 1s | 快速成长,仍在完善 | 追求小体积高性能,Web 团队 + Rust 支撑 |
| Wails | Go | Vue/React/任意 Web | 8MB - 20MB | 30MB - 80MB | 0.4s - 1s | 社区中等 | Go 团队,想摆脱 Electron 体积包袱 |
| Flutter Desktop | Dart | Flutter Widget | 10MB - 20MB | 50MB - 120MB | 0.5s - 1s | 移动端沉淀后逐步成熟 | 移动端团队统一技术栈 |
| PySide6 | Python | QML / Widgets | 100MB - 200MB | 80MB - 150MB | 1s - 2s | 背靠 Qt,稳定但繁琐 | Python 生态工具类、内部系统 |
| Qt/C++ | C++ | QML / Widgets | 10MB - 50MB(可裁剪) | 50MB - 100MB | 0.3s - 0.8s | 非常成熟 | 性能要求极高,长期重投入 |
这张表里的数字是数量级参考,不同项目会因为依赖多少、是否压缩而有浮动。但一个很明显的趋势是:凡是复用系统 WebView 的方案,安装包体积都小一个量级;凡是自带浏览器或者自带解释器的方案,体积都下不来。理解了这一点,后面的选型思路就很清晰了。
2. 为什么 Electron 安装包直奔 224MB,而 Rust + Vue 能压到 4.7MB
很多做前端的朋友第一次听说 Tauri 能把安装包做到几 MB,第一反应是“假的吧”。其实只要搞懂 Electron 那 200 多 MB 到底装了什么,再回头看 Tauri 的体积逻辑,你就知道这不是魔术,纯粹是架构设计上的降维打击。
2.1 Electron 的体积来源:每个用户都重新下载一个“Chrome + Node”
Electron 安装包的体积构成几乎没有秘密:它内置了完整的 Chromium 渲染引擎,这套东西在 Windows 上差不多要占 100MB 以上,macOS 上差不多 70MB 起步;再加上 Node.js 运行时,几十 MB;然后是你的业务代码、依赖库、图标、资源文件,七七八八加起来,轻松突破 200MB。我那个朋友的项目里还集成了一堆音视频处理、文件解析相关的 Node 原生模块,每个都要把动态链接库塞进包里。
有人可能会说,那 Electron 的体积大就大一点呗,反正现在固态硬盘便宜、带宽也快。问题在于这 200 多 MB 不是装一次就完了,而是每个用户都要重新下载一遍。拿一个 10 万日活的应用来算,每次发版至少要分发 20TB 级别的流量,CDN 账单、用户下载时长、安装失败率,都是实打实的成本。用户从点击下载到打开应用往往要等一两分钟,心理上很容易流失。
224MB 这个数字还不是最夸张的。我在一些企业内网系统里见过接近 400MB 的 Electron 安装包,原因是里面塞了各种自定义字体、离线地图、大体积模型文件。Electron 的架构决定了你很难把体积控制在 100MB 以内,因为你动的只是业务那几 MB,而浏览器内核的底子是动不了的。
2.2 Tauri 的体积逻辑:不重复造浏览器,直接借用系统 WebView
Tauri 的聪明之处在于,它把 Chromium 这个“重资产”从安装包里拿掉了。Windows 上调用系统自带的 WebView2(也就是 Edge 的渲染内核),macOS 上调用 WKWebView,Linux 上调用 WebKitGTK。这些组件绝大多数用户的系统里已经有了,不需要你的安装包再带一份。
于是 Tauri 安装包里剩下来的主要就是两样东西:Rust 编译出来的原生二进制程序,以及前端构建后的静态资源。Rust 的二进制在采用体积优化编译后可以做到很小,一个完整的桌面应用主程序压缩前可能就 5MB 上下,压缩后更小。而 Vue 这类前端框架构建出来的产物,对于一个中等复杂的界面来说,通常也就几百 KB 到 1MB 左右。
你可能要问:那 4.7MB 是怎么来的?以我朋友那个项目为例,前端资源大概 500KB,Rust 二进制压缩后在 3MB 左右,再加上安装脚本、基础配置、图标,最后拿 NSIS 或者 AppImage 打完包,就是 4.7MB。如果继续做极端优化,关掉默认没用的功能模块、用 UPX 这类工具再压一层,甚至能逼近 3MB。但我的建议是不要为了数字好看而牺牲稳定性和兼容性,4.7MB 已经是一个很健康的水平了。
2.3 比体积更重要的两个数字:内存占用和启动速度
体积大只是第一层痛点,真正让 Electron 被诟病的还有内存和启动速度。因为 Electron 本质上是一个跑在你自己设备上的完整浏览器,它要加载渲染进程、GPU 进程、网络进程等一系列子进程,一个空窗口吃 150MB 内存是家常便饭。
Tauri 复用系统 WebView 之后,省掉了整套进程架构,只保留渲染界面所需的最小 WebView 实例和 Rust 后端。我实测下来,同样一个 Vue 页面,Electron 空应用内存占用在 180MB 左右,Tauri 只有 50MB 上下。如果你做的是常驻后台的桌面工具,几十台电脑合在一起,内存节省非常可观。
启动速度上差距也很明显。Electron 冷启动要拉起 Chromium 那一堆初始化流程,再加上解析 Node 模块,1.5 秒起跳是常态。Tauri 只需要启动 Rust 进程并调用系统 WebView,冷启动基本在 500 毫秒以内,用户点击图标的感知是完全不同的。用一个不恰当但很生动的类比:Electron 是开一辆大房车出门,什么都有,但启动、耗油都是开销;Tauri 是开一辆小车,路上用已有的服务区,轻装快跑。
3. 实操:Tauri + Vue 从零搭建到打包出 4.7MB 安装包
理论说得再多,不如动手跑一遍。这一节我把 Tauri + Vue 从环境准备到最终打包的完整流程写出来,包含我踩过的坑和常用的瘦身参数。这套流程在 Windows、macOS、Linux 上大差不差,我以 Windows 环境为主,需要跨平台的差异我会单独标出来。
3.1 环境准备:Rust、Node、平台依赖一个都不能少
第一步是装 Rust。官方推荐用 rustup 工具链安装,在终端执行 rustup-init.exe,一路默认就行。装完以后确认一下 cargo 和 rustc 都在 PATH 里:
cargo --version rustc --versionRust 工具链本身就比较大,如果网络条件一般可能会比较慢,耐心等待即可。装完 Rust 之后就是 Node.js,建议直接装最新 LTS 版本,我这边用的是 18 以上的版本,配合 pnpm 或者 npm 都行。Vue 项目本身需要 Node 环境来跑 Vite 开发服务器和打包前端资源。
然后处理平台相关的依赖。Windows 上需要确保系统有 WebView2 运行时,Win10 1803 以上基本都自带;还需要安装 Microsoft C++ Build Tools,因为 Rust 的某些原生依赖需要 MSVC 编译。macOS 上只需安装 Xcode Command Line Tools。Linux 上最麻烦,需要安装 WebKitGTK 等一堆开发库,Ubuntu 系的命令大概是:
sudo apt update sudo apt install libwebkit2gtk-4.0-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev这里特别提醒一下,很多人在 Linux 上打包失败,就是提前没装全这些依赖。别等到编译到一半报找不到 webkit2gtk 再回来补,那会非常痛苦。
3.2 用官方脚手架创建项目并跑通开发模式
环境准备好之后,用官方脚手架初始化项目。输入下面命令,按提示选择 Vue + TypeScript 模板:
npm create tauri-app@latest cd 你的项目名 npm install npm run tauri dev命令跑完之后,你会看到一个 Tauri 窗口弹出,里面渲染的是 Vue 页面。这个窗口由系统 WebView 渲染,而不是一个完整的浏览器窗口,这也是它轻量的根本原因。
项目结构上,根目录的 src 文件夹是前端 Vue 代码,src-tauri 文件夹是 Rust 后端代码,核心配置在 src-tauri/tauri.conf.json 里。开发状态下,Tauri 会启动 Vite 开发服务器,然后用 WebView 加载 http://localhost:5173 这个地址;而生产打包时,它会自动把 dist 目录下的前端资源内嵌进二进制,不依赖外网服务器。
对于 Vue 工程师来说,前端部分和以前写 Web 应用没有任何区别,唯一的注意点是 Vue Router 不要用 history 模式。这里再啰嗦一句:如果你的项目用了 createWebHistory,Tauri 里刷新页面很容易白屏,因为生产环境下没有传统服务器去做 URL fallback。最简单粗暴的解决办法是用 createWebHashHistory,后面第 5 节我再细讲。
3.3 打包配置:Cargo 瘦身参数与 Tauri 打包目标
开发跑通之后,就可以开始准备打包了。首先打开 src-tauri/Cargo.toml,在文件末尾追加一个 release profile,这对最终安装包体积影响极大:
[profile.release] codegen-units = 1 lto = true opt-level = "s" panic = "abort" strip = true这几个参数各有用处:
- lto = true 开启链接时优化,让编译器在链接阶段做跨模块优化,能有效减小二进制体积;
- codegen-units = 1 限制并行代码生成单元,虽然会让编译慢一点,但配合 LTO 体积优化更彻底;
- opt-level = "s" 让编译器以“优化体积”为目标生成代码,而不是默认的速度优先;
- panic = "abort" 去掉 Rust 的堆栈展开信息;
- strip = true 直接剥离符号表,对减小体积帮助极大。
接着看 src-tauri/tauri.conf.json,这块决定你要产出什么格式的安装包。Windows 上一般用 nsis,Linux 上常用 deb 或者 AppImage,macOS 用 dmg。我的习惯是:
"bundle": { "active": true, "targets": ["nsis"], "icon": ["icons/icon.ico"] }如果你在 Linux 上打 deb 包,可能会遇到 fpm 相关的报错,这个我在第 5 节单独讲。打包前先用 cargo check 和前端 build 确认没报错,再执行:
npm run tauri build第一次编译会特别慢,因为要把几百个 Rust crate 全部拉取并编译一遍,卡个几分钟甚至十几分钟都很正常。编译完成后在 src-tauri/target/release/bundle/ 目录下就能看到安装包文件。我朋友的项目在这个状态下还没做体积优化,只启用了默认配置,打包出来大概 8MB 多,做完瘦身之后才压到 4.7MB。
3.4 前端资源与二进制体积的平衡
很多人以为 Tauri 体积小,前端构建产物肯定也做了极端压缩。这里我给一个中肯的建议:前端部分不要刻意追求极致压缩,保证合理的构建优化就够了。Vite 默认就是用 Rollup 做生产构建,已经会压缩混淆 JS 和 CSS,对于 Vue 项目来说,产物通常在几百 KB 到一两 MB 之间。前端资源会被 Tauri 以某种形式嵌入到最终二进制里,所以前端体积越大,安装包也越大。
实际操作中,我会定期检查 src-tauri/target 目录下生成的 exe 或二进制文件大小,再对比 dist 目录的前端产物大小。如果发现二进制文件暴涨,优先检查是不是在 main.rs 里引入了太大的依赖,或者是图标资源没有压缩。Tauri 本身的默认功能模块很多,如果你不需要某些插件(比如自动更新、隧道穿透等),在 Cargo.toml 里去掉对应 feature,也能省下一些体积。但注意不要一上来就全套裁减,等打包流程稳定之后再逐步做减法。
4. 其他五种方案的打包实战与体积实录
横评的意义在于对照。光看 Tauri 一家好不够,我把其他几种方案各自的打包体感和体积都拉出来遛一遛,这样你在选型的时候才有完整的坐标系。
4.1 Electron 的 224MB 怎么来的,能瘦到多少
Electron 项目用 electron-builder 打包是主流方案。一个 Vue + Electron 项目,打包产物里至少有:electron.exe 和配套的 dll(120MB 上下),app.asar(若干 MB 到几十 MB,取决于业务和依赖),各种语言包和资源文件。即便你用 asar 压缩把项目源码和 node_modules 塞进一个包,体量仍然非常大,因为大头的 chrome_100_percent.pak、icudtl.dat 这些资源是省不掉的。
我试过给 Electron 做瘦身,发现能动的只有业务代码和依赖,核心运行时根本动不了。一个空 Electron 应用打包出来最少也要 60MB 左右,稍微加几个依赖就上 150MB。反过来想,224MB 其实不是某个人的打包姿势有问题,而是这套架构的基线太高。如果你想在 Electron 体系里把安装包压到 100MB 以下,唯一现实的路线是引入按需下载 CDN 资源的机制,但那是把产品架构搞复杂,跟桌面端“一键安装离线可用”的初衷冲突了。
4.2 Wails:Go + Vue,轻量方案的第二梯队
Wails 和 Tauri 思路几乎一致,也是复用系统 WebView,区别在后端用 Go。我尝试过一个 Wails + Vue 的简单项目,打包出来 9MB 左右,比 Electron 强太多,在开发体验上,Go 的编译速度和简单语法对后端工程师非常友好。Wails 的初始化命令也简单:
wails init -n myproject -t vue wails build体积方面,Wails 比 Tauri 大一些,主要因为 Go 的运行时包含 GC 和调度器,静态链接的二进制体积天然比 Rust 大。但如果你团队的主力语言是 Go,这个差距完全可以通过开发效率弥补。它和 Tauri 的最大区别不在性能,而在 Rust 和 Go 之间的选择:Rust 更节省资源但也更考验工程师,Go 上手快、坑相对少。
4.3 Flutter Desktop 与 PySide6 的真实体感
Flutter Desktop 这两年势头很猛,用 Dart 编写,UI 是自绘引擎渲染,不依赖系统 WebView,所以渲染一致性非常强。打包一个简单的桌面应用,体积大概 10-20MB,内存占用比 Tauri 高一些,但比 Electron 好很多。如果你们团队本来就是 Flutter 移动端出身,顺手做桌面是首选。
PySide6 则是另一条路线。Python 开发工具类应用确实快,Qt 的控件系统也成熟,但打包是绕不过去的痛点:Python 解释器、PySide6 的 Qt 绑定库、各种依赖全部要打包进去,安装包很容易超过 150MB。更难受的是,Qt 的库文件非常多,即便你用 PyInstaller 打包,最后得到一堆散文件,分发体验远不如单文件安装包。我自己的结论是:PySide6 适合做内部工具或脚本型桌面应用,真要对外发布给海量用户,体积这道坎很难迈过去。
4.4 Qt/C++ 和 Rust 原生 UI 方案的体积下限
如果你追求极致体积,Qt/C++ 是一条路。Qt 支持静态编译,把用到的库全部揉进一个可执行文件,一个简单的界面程序几 MB 就能搞定。但 Qt 静态编译存在许可和工程复杂度问题,而且整个编译配置非常折腾,小团队未必承受得起。Rust 生态里也有 egui 这类纯原生 UI 框架,二进制体积可以做到 1MB 以内,但 UI 的表达能力比 Web 技术栈弱不少,适合做调试面板、监控工具这类偏工具型的界面,而不是业务复杂的客户端。
5. 常见问题与排查技巧:Linux 打包、路由适配、编译踩坑
任何工具链都有一堆“文档里没写明白”的坑,Tauri 也不例外。我把自己实际遇到过的、以及身边同事反馈频率最高的问题整理成一份速查,省得你重复踩雷。
5.1 Linux 打包时 fpm 报错怎么办
标题里那个朋友的项目在 Linux 上打包 deb 时就遇到过 fpm 报错。fpm 是 Ruby 写的打包工具,Tauri 在生成 deb/rpm 包时会调用它。常见的报错原因包括:系统 Ruby 版本太老、fpm 依赖的 gem 没装全、没有写权限等。
我的建议是:能不用 fpm 就不要手动缠斗。Tauri 在 Linux 下优先打 AppImage,AppImage 的好处是不依赖系统库,分发也简单。如果你的目标发行版是 Ubuntu/Debian 系,打 deb 包之前确保系统里有 ruby-dev 和 fpm,或者直接把打包放到 CI 的 Docker 容器里跑。Tauri 官方提供了 tauri-action 的 GitHub Action,里面有预配置的 Linux 打包环境,能省掉一堆本地环境问题。
5.2 Vue 路由在 Tauri 里刷新白屏的坑
这个问题几乎每个用 Vue Router 的 Tauri 项目都会遇到。开发环境没问题,但打包后一刷新页面就白屏。原因是生产环境下 Tauri 用的是自定义协议加载前端资源,根本没有服务端来处理 URL 路径。解决办法很简单:用 hash 模式而不是 history 模式:
import { createRouter, createWebHashHistory } from 'vue-router' const router = createRouter({ history: createWebHashHistory(), routes })如果你硬要用 history 模式,就得在 Rust 侧写自定义协议处理 fallback,复杂度和收益不成正比。反正桌面端应用不依赖 URL 的语义化,hash 模式是最稳的选择。
5.3 Windows 打包的图标、签名和路径问题
Windows 打包踩坑最多的三个点:图标、签名、路径。Tauri 对图标格式有硬性要求,Windows 必须提供 .ico,macOS 必须提供 .icns,Linux 提供多个尺寸的 .png。官方推荐用npm run tauri icon命令从一张 1024x1024 的 PNG 自动生成全套图标,千万不要自己拿 jpg 改后缀名糊弄。另外项目路径里不要有中文,NSIS 脚本对非 ASCII 路径支持不好,这个坑我踩过一次,后来养成了所有项目一律英文路径的习惯。代码签名这块,如果你要公开发布,Windows SmartScreen 会拦截没有签名的安装包,Tauri 官方文档有签名配置说明,建议在 CI 里集成,不要拖到发布前才处理。
5.4 Rust 首次编译慢、学习门槛高怎么办
很多前端项目组对 Tauri 望而生畏,主要怕 Rust。先说编译慢的问题,首次编译要拉取并编译所有依赖,慢是正常的,但后续增量编译会快很多。建议在用户的设置里给 cargo 配置镜像加速依赖下载,另外官方脚手架生成的项目默认就配了缓存机制,没必要提前优化。Rust 语言本身确实比 JavaScript 硬核,但 Tauri 日常开发里你用到的 Rust 只是冰山一角:无非是创建窗口、注册命令、读写文件,再加上几个结构体和错误处理。我建议前端工程师从这本书入手,看到 async、future、生命周期这些概念时不用恋战,先用起来,等真的碰到性能瓶颈再回来深入研究。Rust 安全性的心智负担在高并发系统里才是主角,在 Tauri 这种场景里你完全可以把后端写得像简单脚本一样直白。
6. 选型建议:什么时候选 Electron,什么时候选 Tauri
写了这么多对比,最后还是得落到真实决策上。选型没有绝对正确,只有适合不适合。
如果你的团队是纯前端出身,业务又特别复杂,比如要做在线文档、IDE、复杂富文本编辑器这类系统,还要大量使用 Node.js 生态里的原生模块,那 Electron 仍然是最稳的选择。VS Code 就是典型例子,它靠 Electron 做到了一版代码全平台跑,积累的周边生态无可替代。这个赛道里,体积和内存不是核心矛盾,迭代速度和稳定性才是。
但如果你做的是面向消费者的工具类软件,比如视频播放器、桌面笔记、聊天工具、文件管理工具,用户对安装包体积和启动速度特别敏感,前端又有 Vue/React 技术沉淀,那 Tauri 是一个非常值得押注的方向。它保留了 Web 技术栈的开发效率,同时把体积和内存两条指标拉回原生水准。前提是团队里有一两个人能扛住 Rust 后端,或者愿意花两周左右时间补 Rust 基础。
至于中间情况,Go 团队选 Wails,移动端团队选 Flutter Desktop,Python 团队做内部工具选 PySide6,性能极致要求且有长期投入能力的选 Qt/C++。这些选择都不丢人,关键是搞清楚自己的约束条件:时间、人员技术栈、目标用户网络环境、发布渠道平台,列出来以后一对照,答案基本就出来了。
我个人的体会是,Tauri 不是银弹,它的坑并不比 Electron 少,尤其是 Linux 打包和系统 WebView 兼容性上,有时候你会怀念 Electron 的“一套 Chromium 走天下”。但当你把安装包从 224MB 压到 4.7MB,亲眼看到用户下载耗时从几分钟变成几秒钟,就会觉得这些折腾都值了。最后再分享一个小技巧:不管最后选哪个方案,都别只看官方文档,把目标平台的用户环境摸清楚——系统版本分布、网络带宽、是否常驻内存,这些才是决定桌面端体验的真正变量。