news 2026/9/19 9:59:14

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从224MB到4.7MB:Electron与Tauri等六种跨平台桌面方案实测横评

1. 从 224MB 到 4.7MB:一个让我彻底抛弃 Electron 的实测横评

去年年底我接手了一个内部工具项目,需求很朴素:一个能在 Windows、macOS、Linux 上跑的桌面客户端,界面用 Vue 写,功能就是本地文件处理加一些数据可视化。我第一反应还是 Electron,毕竟用了五六年了,闭着眼都能搭起来。结果打包出来一看,Windows 安装包 224MB,macOS 的 dmg 也有 180 多兆。发给同事测试的时候,对方在群里回了一句“你这发的是个游戏吗”,那一刻我是真的有点绷不住。

后来我花了大概三周时间,把市面上能叫得上名字的跨平台桌面方案挨个试了一遍,最终用 Rust + Vue 的组合把安装包压到了 4.7MB。这篇文章不是要无脑吹某个方案,而是把我这轮横评的完整过程、每个方案的取舍逻辑、踩过的坑和最终选型理由全部摊开讲。如果你也在纠结桌面端技术选型,或者单纯想知道那 200 多兆到底装了什么、怎么才能砍掉,这篇应该能帮你省下不少试错时间。

先交代一下我的评测环境:主力开发机是 macOS(M 系列芯片),测试机包括一台 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-builderasar打包、electron-packager裁剪,但 Chromium 内核那 100 多兆是砍不掉的,那是它的根基。

安装包大还只是第一层问题。启动之后内存占用也不客气,一个简单窗口轻松吃掉 200MB 以上内存。在配置老一点的机器上,冷启动要等三四秒才能看到界面。我试过用electron-vite做构建优化,用electron-buildercompression: maximum压缩,最终也就压到 180MB 左右,再往下就动不了了。

不过话说回来,Electron 的生态是真的强。electron-store做配置持久化、electron-updater做自动更新、electron-log做日志,这些轮子都磨得很成熟。如果你团队全是前端、项目周期紧、对体积不敏感,Electron 依然是最稳的选择。我现在的态度是:能用,但要看场景。

2.2 Tauri:Rust 加持的体积杀手

Tauri 是我这轮横评的重点。它的思路和 Electron 完全相反:不打包浏览器内核,而是用系统自带的 WebView。Windows 上用 WebView2,macOS 上用 WKWebView,Linux 上用 WebKitGTK。前端还是写 Vue,但后端逻辑用 Rust 写,通过命令(command)机制通信。

这个架构带来的体积优势是碾压性的。我那个同样的项目,Tauri 打包出来 Windows 安装包 4.7MB,macOS 的 dmg 大概 6MB 出头。为什么能这么小?因为 WebView 是系统提供的,不占你的包体;Rust 编译出来的二进制本身就很紧凑,而且没有运行时依赖。

但 Tauri 不是没有代价。首先你得会一点 Rust,虽然不用写很多,但至少得能看懂#[tauri::command]Resultasync这些基本概念。其次,不同系统的 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 Wails:Go 语言版的 Tauri

Wails 的思路和 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 224MB,Tauri 只有 4.7MB?这个差距不是魔法,而是架构决定的。我把核心差异整理成表,你一看就明白。

对比维度ElectronTauriWailsFlutterQtNeutralino
渲染引擎自带 Chromium系统 WebView系统 WebView自带 Skia自带 Qt系统 WebView
后端语言Node.jsRustGoDartC++/QMLC++
基础包体积150MB+3-5MB6-10MB25-35MB20-40MB1-2MB
内存占用极低
前端框架任意任意任意FlutterQML任意
学习成本中高
生态成熟度极高极高

核心结论就一句话:Electron 把浏览器内核打包进去了,其他方案用的是系统已有的 WebView 或自绘引擎。Chromium 内核压缩后大概 100-130MB,这就是 Electron 体积的底座。你写的业务代码可能只有几兆,但底座砍不掉。

Tauri 之所以能做到 4.7MB,是因为它只打包了 Rust 编译的二进制和你的前端资源,WebView 用系统的。Rust 二进制本身很小,而且没有 GC 运行时,静态链接后非常紧凑。我实测那个 4.7MB 的包里,前端资源大概 1.2MB,Rust 二进制 3MB 左右,剩下的是图标和配置。

4. Rust + Vue 实战:从零搭一个 4.7MB 的桌面应用

4.1 环境准备与项目初始化

先说环境。Rust 安装直接去官网下rustup,一路默认就行。装完之后rustc --versioncargo --version能出来就说明好了。Vue 这边我用的 Vite 模板,npm create vite@latest选 Vue + TypeScript,几秒钟就搭好了。

Tauri 的脚手架推荐用create-tauri-app,命令是:

npm create tauri-app@latest

它会问你项目名、前端框架、包管理器,选 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 build"frontendDist: "../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) -> Result<String, 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]是宏,把普通函数标记成可以被前端调用的命令。返回类型用Result<String, String>,成功返回文件内容,失败返回错误信息字符串。map_errstd::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 = "2"

main.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.jsonbundle字段里。我重点调了几个参数。

首先是targets,Windows 上我选nsis,macOS 上选dmg,Linux 上选debappimagensis的压缩率比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 build

Windows 上跑完,安装包在src-tauri/target/release/bundle/nsis/目录下,我那个项目是 4.7MB。macOS 的在bundle/dmg/下,6.2MB。这个体积发微信、发邮件都毫无压力。

5. 踩坑实录:那些文档里不会写的问题

5.1 WebView 版本差异导致的样式翻车

我在 macOS 上开发的时候,界面一切正常。打包给 Windows 同事测试,对方反馈说毛玻璃效果没了,圆角也变成了直角。排查了半天,发现是 Windows 10 自带的 WebView2 版本太老,不支持backdrop-filterborder-radius的某些组合。

解决办法有两个:一是引导用户更新 WebView2,Tauri 的安装包可以配置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 依赖webkit2gtklibgtk-3-devlibayatana-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 运行时配置webviewInstallModedownloadBootstrapper
Linux 编译失败缺少 webkit2gtk 依赖apt install libwebkit2gtk-4.1-dev
前端调用 invoke 无响应命令未注册或参数名不匹配检查generate_handler和参数命名
安装包体积异常大未开启 release 优化检查Cargo.toml的 profile 配置
macOS 签名失败未配置签名证书开发阶段可跳过签名,发布时配置
窗口拖拽区域失效CSS 的-webkit-app-region未设置给拖拽区域加>
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 9:58:09

CANN Runtime TDT 数据传输接口详解:Tensor 通道创建、发送与接收

CANN Runtime TDT 数据传输接口详解&#xff1a;Tensor 通道创建、发送与接收 【免费下载链接】runtime 本项目提供CANN运行时组件和维测功能组件。 项目地址: https://gitcode.com/cann/runtime 导读 本文围绕 CANN Runtime 中的 TDT&#xff08;Tensor Data Transfer…

作者头像 李华
网站建设 2026/9/19 9:56:44

AI大模型本地部署与工程实践:从配置到应用的全链路指南

9月13日的AI日报来了。我照例先扫了一遍热搜词列表&#xff0c;热度最集中的几个方向其实很有代表性&#xff1a;AI大模型、大模型本地部署配置、AI编程、AI应用开发、AI视频和AI短剧。如果你正准备入局AI应用开发&#xff0c;或者像我一样在折腾本地模型部署&#xff0c;这份日…

作者头像 李华
网站建设 2026/9/19 9:56:14

FLORR.IO 画廊(2):用 WebGL 与参数化设计生成数字花卉作品

从你第一次在时间线上刷到那条花瓣缓缓旋转的短片开始&#xff0c;我就知道会有不少人和我一样&#xff0c;在浏览器标签页里蹲了一晚上&#xff0c;就为了调出自己满意的那朵花。FLORR.IO 这类基于 WebGL 的数字花卉生成工具&#xff0c;最近在创意编程和视觉艺术的小圈子里热…

作者头像 李华
网站建设 2026/9/19 9:55:19

Fastp实战指南:从参数配置到批量处理,全面掌握fastq质控流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华

关于博客

这是一个专注于编程技术分享的极简博客,旨在为开发者提供高质量的技术文章和教程。

订阅更新

输入您的邮箱,获取最新文章更新。

© 2025 极简编程博客. 保留所有权利.