ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

从224MB到4.7MB:Electron与Tauri等六种跨平台桌面方案实测横评

从224MB到4.7MB:Electron与Tauri等六种跨平台桌面方案实测横评 1. 从 224MB 到 4.7MB一个让我彻底抛弃 Electron 的实测横评去年年底我接手了一个内部工具项目需求很朴素一个能在 Windows、macOS、Linux 上跑的桌面客户端界面用 Vue 写功能就是本地文件处理加一些数据可视化。我第一反应还是 Electron毕竟用了五六年了闭着眼都能搭起来。结果打包出来一看Windows 安装包 224MBmacOS 的 dmg 也有 180 多兆。发给同事测试的时候对方在群里回了一句“你这发的是个游戏吗”那一刻我是真的有点绷不住。后来我花了大概三周时间把市面上能叫得上名字的跨平台桌面方案挨个试了一遍最终用 Rust Vue 的组合把安装包压到了 4.7MB。这篇文章不是要无脑吹某个方案而是把我这轮横评的完整过程、每个方案的取舍逻辑、踩过的坑和最终选型理由全部摊开讲。如果你也在纠结桌面端技术选型或者单纯想知道那 200 多兆到底装了什么、怎么才能砍掉这篇应该能帮你省下不少试错时间。先交代一下我的评测环境主力开发机是 macOSM 系列芯片测试机包括一台 Windows 11 台式、一台 Ubuntu 22.04 虚拟机以及一台配置比较老的 Windows 10 笔记本。所有方案都实现同一个最小可用功能一个窗口、一个 Vue 写的界面、调用系统文件对话框选文件、读取文件内容并展示、打包成安装包。这样对比才公平。2. 六种跨平台桌面方案逐一拆解2.1 Electron生态最成熟但体积是原罪Electron 的原理说白了就是把 Chromium 和 Node.js 各塞一份进你的安装包然后用它们渲染网页。你写的 Vue 代码本质上跑在一个精简版浏览器里通过 IPC 和主进程通信。这个架构的好处是前端开发者零学习成本Node 生态随便用什么 fs、path、child_process 直接调。但代价也很直接。一个空白的 Electron 项目什么都不装打包出来 Windows 安装包就在 150MB 上下。我那个项目因为用了几个图表库和文件处理库直接飙到 224MB。你可以用electron-builder的asar打包、electron-packager裁剪但 Chromium 内核那 100 多兆是砍不掉的那是它的根基。安装包大还只是第一层问题。启动之后内存占用也不客气一个简单窗口轻松吃掉 200MB 以上内存。在配置老一点的机器上冷启动要等三四秒才能看到界面。我试过用electron-vite做构建优化用electron-builder的compression: maximum压缩最终也就压到 180MB 左右再往下就动不了了。不过话说回来Electron 的生态是真的强。electron-store做配置持久化、electron-updater做自动更新、electron-log做日志这些轮子都磨得很成熟。如果你团队全是前端、项目周期紧、对体积不敏感Electron 依然是最稳的选择。我现在的态度是能用但要看场景。2.2 TauriRust 加持的体积杀手Tauri 是我这轮横评的重点。它的思路和 Electron 完全相反不打包浏览器内核而是用系统自带的 WebView。Windows 上用 WebView2macOS 上用 WKWebViewLinux 上用 WebKitGTK。前端还是写 Vue但后端逻辑用 Rust 写通过命令command机制通信。这个架构带来的体积优势是碾压性的。我那个同样的项目Tauri 打包出来 Windows 安装包 4.7MBmacOS 的 dmg 大概 6MB 出头。为什么能这么小因为 WebView 是系统提供的不占你的包体Rust 编译出来的二进制本身就很紧凑而且没有运行时依赖。但 Tauri 不是没有代价。首先你得会一点 Rust虽然不用写很多但至少得能看懂#[tauri::command]、Result、async这些基本概念。其次不同系统的 WebView 版本不一样渲染表现会有细微差异尤其是 CSS 的一些新特性在老版本 WebView2 上可能不支持。我实测下来Windows 10 自带的 WebView2 如果没更新某些backdrop-filter效果会失效得做降级处理。还有一个坑是 Linux 上的依赖问题。Tauri 在 Linux 上依赖webkit2gtk不同发行版的版本差异很大。我在 Ubuntu 22.04 上编译很顺利但在一个较老的 CentOS 上折腾了半天才把依赖装齐。如果你要覆盖 Linux 用户这块得提前测。2.3 Flutter Desktop一套代码全平台但桌面端还在追赶Flutter 做桌面端这两年进步很快Windows、macOS、Linux 都支持了。它的优势是 UI 一致性极强同一套 Dart 代码在哪个平台渲染出来都一模一样不像 Tauri 那样受系统 WebView 影响。但 Flutter 的问题在于桌面端的生态还不够成熟。很多插件是移动端优先的桌面端要么不支持要么功能残缺。我试了一个文件选择的插件在 macOS 上正常在 Windows 上返回的路径格式有问题得自己处理。而且 Flutter 的安装包也不小一个基础项目 Windows 打包出来 30MB 左右虽然比 Electron 好很多但和 Tauri 比还是差一个数量级。另外 Flutter 用的是 Dart 语言如果你团队是 Vue 技术栈迁移成本不低。我个人的判断是如果你的项目本来就是 Flutter 移动端想顺便出个桌面版那很合适如果从零开始且团队是前端背景Tauri 更顺。2.4 Qt老牌劲旅但和前端生态隔着一道墙Qt 在桌面端是真正的老大哥C 写的性能没得说跨平台支持也最完善。Qt 6 之后对 QML 的支持更好了可以用类似 JavaScript 的语法写界面。但 Qt 的问题在于它和现代前端生态几乎是两个世界。你想用 Vue 写界面基本不可能得用 QML 重写。而且 Qt 的授权模式比较复杂商业项目要注意 LGPL 和商业授权的区别。我试了一下 Qt光是配环境就花了大半天编译速度也慢。对于我这种前端背景的人来说学习曲线太陡了。不过如果你的项目对性能要求极高比如涉及大量实时图形渲染、视频处理Qt 依然是首选。它的原生渲染能力是 WebView 方案比不了的。2.5 WailsGo 语言版的 TauriWails 的思路和 Tauri 很像也是用系统 WebView只不过后端语言是 Go。如果你团队本来就写 Go那 Wails 会很顺手。它的 API 设计很简洁绑定 Go 结构体到前端直接调用体验不错。体积上 Wails 也很优秀基础项目 Windows 打包出来 8MB 左右比 Tauri 稍大一点但完全可接受。不过 Wails 的生态和社区规模比 Tauri 小不少遇到问题查资料会费劲一些。我在测试时遇到一个窗口拖拽区域的问题搜了半天才在 GitHub issue 里找到答案。2.6 Neutralino极致轻量但功能也极简Neutralino 是这六个里最轻的安装包可以做到 2MB 以内。它的原理更极端连 WebView 都不打包直接用系统的后端是一个轻量级的 C 二进制。但 Neutralino 的功能也最基础很多系统 API 要么没有要么得自己写扩展。我试了一下连文件对话框都得自己调系统命令实现。它适合那种极简工具比如一个本地小脚本的图形界面但不适合正经的桌面应用开发。3. 体积差异的根源一张表看清本质为什么 Electron 224MBTauri 只有 4.7MB这个差距不是魔法而是架构决定的。我把核心差异整理成表你一看就明白。对比维度ElectronTauriWailsFlutterQtNeutralino渲染引擎自带 Chromium系统 WebView系统 WebView自带 Skia自带 Qt系统 WebView后端语言Node.jsRustGoDartC/QMLC基础包体积150MB3-5MB6-10MB25-35MB20-40MB1-2MB内存占用高低低中中极低前端框架任意任意任意FlutterQML任意学习成本低中中中高高中生态成熟度极高高中中极高低核心结论就一句话Electron 把浏览器内核打包进去了其他方案用的是系统已有的 WebView 或自绘引擎。Chromium 内核压缩后大概 100-130MB这就是 Electron 体积的底座。你写的业务代码可能只有几兆但底座砍不掉。Tauri 之所以能做到 4.7MB是因为它只打包了 Rust 编译的二进制和你的前端资源WebView 用系统的。Rust 二进制本身很小而且没有 GC 运行时静态链接后非常紧凑。我实测那个 4.7MB 的包里前端资源大概 1.2MBRust 二进制 3MB 左右剩下的是图标和配置。4. Rust Vue 实战从零搭一个 4.7MB 的桌面应用4.1 环境准备与项目初始化先说环境。Rust 安装直接去官网下rustup一路默认就行。装完之后rustc --version和cargo --version能出来就说明好了。Vue 这边我用的 Vite 模板npm create vitelatest选 Vue TypeScript几秒钟就搭好了。Tauri 的脚手架推荐用create-tauri-app命令是npm create tauri-applatest它会问你项目名、前端框架、包管理器选 Vue 和 npm 就行。生成的项目结构大概是这样的my-app/ ├── src/ # Vue 前端代码 ├── src-tauri/ # Rust 后端代码 │ ├── src/ │ │ └── main.rs │ ├── Cargo.toml │ └── tauri.conf.json ├── package.json └── vite.config.ts这里有个细节要注意tauri.conf.json里的build配置决定了打包时前端资源怎么处理。默认是beforeBuildCommand: npm run buildfrontendDist: ../dist意思是先构建 Vue再把 dist 目录塞进包里。这个流程很顺不用改。4.2 用 Rust 写一个文件读取命令Tauri 的核心通信机制是命令。前端通过invoke调用 Rust 函数Rust 处理完返回结果。我以读取文件为例展示完整流程。在src-tauri/src/main.rs里加一个命令use std::fs; #[tauri::command] fn read_file(path: String) - ResultString, String { fs::read_to_string(path).map_err(|e| e.to_string()) } fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![read_file]) .run(tauri::generate_context!()) .expect(error while running tauri application); }这段代码有几个点值得说。#[tauri::command]是宏把普通函数标记成可以被前端调用的命令。返回类型用ResultString, String成功返回文件内容失败返回错误信息字符串。map_err把std::io::Error转成String因为 Tauri 要求错误类型可序列化。前端调用就很简单了import { invoke } from tauri-apps/api/core; async function loadFile(path) { try { const content await invoke(read_file, { path }); console.log(content); } catch (e) { console.error(读取失败:, e); } }注意参数名path要和 Rust 函数的参数名一致Tauri 会自动做映射。这个机制比 Electron 的 IPC 清爽很多不用手动序列化反序列化。4.3 文件对话框与系统集成Tauri 官方提供了tauri-apps/plugin-dialog插件装完之后在tauri.conf.json里注册权限就能调系统文件对话框了。npm install tauri-apps/plugin-dialog然后在src-tauri/Cargo.toml里加[dependencies] tauri-plugin-dialog 2main.rs里注册插件tauri::Builder::default() .plugin(tauri_plugin_dialog::init()) .invoke_handler(tauri::generate_handler![read_file]) .run(tauri::generate_context!()) .expect(error while running tauri application);前端使用import { open } from tauri-apps/plugin-dialog; async function pickFile() { const path await open({ multiple: false, filters: [{ name: 文本文件, extensions: [txt, md, json] }] }); if (path) { const content await invoke(read_file, { path }); return content; } }这套流程跑通之后一个能选文件、读内容、展示的桌面应用就成型了。整个过程我大概花了一个下午其中一半时间在查 Tauri 2.0 的 API 变化因为网上很多教程还是 1.x 的写法。4.4 打包配置与体积优化打包是重头戏。Tauri 的打包配置在tauri.conf.json的bundle字段里。我重点调了几个参数。首先是targetsWindows 上我选nsismacOS 上选dmgLinux 上选deb和appimage。nsis的压缩率比msi好实测能小 1MB 左右。然后是 Rust 的编译优化。在src-tauri/Cargo.toml里加[profile.release] opt-level z lto true codegen-units 1 panic abort strip true这几个参数的作用分别是opt-level z优先优化体积而不是速度lto true开启链接时优化能去掉未使用的代码codegen-units 1减少并行编译单元让优化更彻底panic abort去掉 panic 时的栈展开代码strip true去掉调试符号。这一套下来Rust 二进制能小 30% 左右。前端这边Vite 的构建默认就做了 tree-shaking 和压缩。我额外配了build.rollupOptions.output.manualChunks把 Vue 单独拆出来避免每次改业务代码都重新打包 Vue。另外图片资源尽量用 SVG字体用系统字体别引入大字体文件。最终打包命令npm run tauri buildWindows 上跑完安装包在src-tauri/target/release/bundle/nsis/目录下我那个项目是 4.7MB。macOS 的在bundle/dmg/下6.2MB。这个体积发微信、发邮件都毫无压力。5. 踩坑实录那些文档里不会写的问题5.1 WebView 版本差异导致的样式翻车我在 macOS 上开发的时候界面一切正常。打包给 Windows 同事测试对方反馈说毛玻璃效果没了圆角也变成了直角。排查了半天发现是 Windows 10 自带的 WebView2 版本太老不支持backdrop-filter和border-radius的某些组合。解决办法有两个一是引导用户更新 WebView2Tauri 的安装包可以配置webviewInstallMode自动下载安装二是在 CSS 里做特性检测用supports做降级。.glass { background: rgba(255, 255, 255, 0.8); } supports (backdrop-filter: blur(10px)) { .glass { background: rgba(255, 255, 255, 0.5); backdrop-filter: blur(10px); } }我最后两个都做了安装包里带上 WebView2 的引导安装同时 CSS 做降级。这样老系统也能用只是视觉效果朴素一点。5.2 Linux 打包的依赖地狱Linux 上的打包是最折腾的。Tauri 依赖webkit2gtk、libgtk-3-dev、libayatana-appindicator3-dev这些系统库。在 Ubuntu 22.04 上apt install一把就装齐了。但在一个较老的发行版上webkit2gtk的版本太低编译直接报错。我的建议是Linux 打包尽量在目标发行版上做或者用 Docker 容器固定环境。Tauri 官方提供了tauri-apps/tauri-action的 GitHub Action可以自动在多个 Linux 发行版上构建省得自己配环境。另外appimage格式虽然通用但在某些系统上需要FUSE支持如果用户没装libfuse2会启动失败。deb包更稳但只适合 Debian 系。我最后两个都出了让用户自己选。5.3 Rust 编译速度与增量构建Rust 的编译速度是出了名的慢。第一次cargo build一个 Tauri 项目可能要等三五分钟。但增量编译之后改一行 Rust 代码重新构建大概十几秒可以接受。有个技巧是开发阶段用npm run tauri dev它会启动一个热重载的开发服务器前端改动即时生效Rust 改动会自动重新编译。但 Rust 编译期间界面会卡住所以尽量把逻辑往前端放Rust 只做必要的系统调用。另外sccache可以缓存 Rust 编译产物多项目开发时能省不少时间。装完之后在~/.cargo/config.toml里配一下rustc-wrapper sccache就行。5.4 常见问题速查表问题现象可能原因解决方法打包报错找不到 WebView2Windows 缺少 WebView2 运行时配置webviewInstallMode为downloadBootstrapperLinux 编译失败缺少 webkit2gtk 依赖apt install libwebkit2gtk-4.1-dev前端调用 invoke 无响应命令未注册或参数名不匹配检查generate_handler和参数命名安装包体积异常大未开启 release 优化检查Cargo.toml的 profile 配置macOS 签名失败未配置签名证书开发阶段可跳过签名发布时配置窗口拖拽区域失效CSS 的-webkit-app-region未设置给拖拽区域加>
返回列表