还在用 Electron?6 种跨平台桌面方案横评:Rust + Vue 把安装包从 224MB 干到 4.7MB

桌面端选型是个吵了十年的话题。前几天我把自己一个 A 股盯盘工具从「Electron 那一套」换成了 Rust + Vue(Tauri 2) ,顺手把市面上主流的 6 种跨平台桌面方案横向比了一遍------不比口号,只比能测出来的东西:安装包多大、靠什么渲染、前端代码能不能直接搬、分发和签名要折腾多少、以后要不要重写。

结论先放这儿:Windows x64 安装包,Electron 系做到了 224 MB 和 104 MB,我这套做到了 4.7 MB。差距不是"优化出来的",是架构决定的。但 Tauri 也不是没有代价,第五节的劝退条款和第七节的踩坑清单,建议你比结论看得更仔细。


一、先把"桌面端开发"拆成三条路线

大多数人争论时把不同物种放在一起比,所以永远吵不出结果。桌面端方案按渲染层怎么来的,其实只有三条路线:

scss 复制代码
① 自带渲染引擎(把浏览器打包进安装包)
   Electron / CEF / NW.js ------ 你发的是"一个私有的 Chromium"

② 用系统 WebView(前端代码 + 宿主语言的本地壳)
   Tauri(Rust) / Wails(Go) / .NET MAUI-WebView ------ 你发的是"一个壳 + 前端产物"

③ 自己画像素 / 用原生控件(不经过浏览器)
   Flutter Desktop(自绘) / Qt(自绘+原生控件) / Avalonia(自绘) / MAUI(原生控件) / 纯原生

三条路线的成本结构完全不同

  • 路线 ① 的成本在分发:每个应用都背一份 Chromium,安装包上百 MB、内存基础开销几百 MB,但换来了"一套代码、全平台渲染完全一致";
  • 路线 ② 的成本在兼容性验证:安装包只有几 MB,但 Windows 用 WebView2(Chromium)、macOS 用 WKWebView(跟系统 Safari 绑定)、Linux 用 WebKitGTK(版本参差),一个 UI 要在三种引擎上验;
  • 路线 ③ 的成本在人力:UI 要重写,前端资产基本归零,但能拿到最一致的绘制结果和最好的性能上限。

选型问题本质上是:你把成本放在分发、验证,还是人力上。


二、6 种主流方案横评(带实测数据)

先上表。数据口径我在第三节说明,避免"某博客说"式引用。

方案 运行时/渲染 开发语言 Windows 安装包量级 前端资产复用 移动端延伸 许可与商业化
Electron 打包 Chromium + Node JS/TS 100--230 MB(实测 VS Code 224 MB、Notion 104 MB) ★★★★★ 完全复用 无(各自另做) MIT,无附加条件
Tauri 2 系统 WebView + Rust 宿主 Rust + 任意前端 4--15 MB(实测 4.7 MB) ★★★★★ 完全复用 ✅ iOS/Android 同一套前端 MIT/Apache-2.0
Wails 系统 WebView + Go 宿主 Go + 任意前端 与 Tauri 同量级(几 MB~十几 MB) ★★★★★ 完全复用 v3 才开始支持 MIT
Flutter Desktop 自绘(Skia/Impeller),不自带浏览器 Dart 20--60 MB 量级 ✗ 要用 Dart 重写 UI ✅ 一套代码五端 BSD-3
.NET MAUI 原生控件抽象层 C#/XAML 视运行时裁剪方式,几十 MB 起 MIT(.NET 开源)
Avalonia 自绘,不依赖系统 UI 框架 C#/XAML 30--70 MB 量级(取决于是否 self-contained) 桌面为主,移动在推 MIT
Qt 6 自绘 + 原生控件 C++/QML(或 PySide) 视部署方式,30--80 MB 开源 LGPLv3 / 商用授权

各版本当前状态(我逐个查了官方源,日期 2026-09-14):

项目 最新稳定版 来源
Tauri(crate) 2.11.5(2.0 稳定版发布于 2024-10-02) crates.io / 官方博客
Electron v44.3.0(2026-09-08) GitHub Releases API
Wails 稳定线 v2.14.0;v3 仍在 beta(v3.0.0-beta.20) GitHub Releases API
Flutter 3.47.4 stable(Dart 3.13.3,2026-09-11) Flutter 官方 release JSON
Avalonia 12.1.2 NuGet
.NET 10.0.12(LTS);11 在 RC Microsoft release metadata
Qt 6.10.3 / 6.11 系列 官方发布页

热度参考(GitHub Star,同日抓取):Electron 123,034 、Tauri 111,042 、Wails 36,230。纯看数字,Tauri 已经追到 Electron 同一个量级------这在两年前不可想象。

逐个说人话

Electron:成熟度没有对手。VS Code、Notion、Slack、Discord 都在用,遇到问题一搜就有答案,主进程/渲染进程模型清晰,Node 全能力在手(串口、打印、注册表、系统 API 都有现成轮子)。代价就是那 100--230 MB:**每个应用背一份 Chromium。**如果你的用户在国内用移动网络下载,224 MB 的安装包和 4.7 MB 的安装包,是完全不同的两个产品体验。

Tauri 2 :Rust 做宿主,前端产物由系统 WebView 渲染。2024 年 10 月 2.0 转正,同时带来 IOC(多平台)支持------同一套 Vue/React 前端可以编到 iOS/Android。它的取舍是明确的:省下的是运行时体积,付出的是多引擎兼容验证。

Wails :Go 团队的 Tauri。架构思路一致(系统 WebView + Go 宿主),v2 稳定、生态齐;v3 至今仍是 beta(beta.20),引入了更现代的多窗口/服务化设计,但我不建议把生产项目压在 beta 上。

Flutter Desktop :唯一"一套代码覆盖桌面+移动+Web 且渲染完全自研"的选项,UI 一致性最好,动画性能高。但它是另一套 UI 生态:你现有的 Vue/React 组件、CSS、图表库全部归零,要用 Dart 重写。桌面端插件生态相比移动端仍然薄。

MAUI / Avalonia:.NET 阵营的两条路。MAUI 抽象原生控件,走的是一致性稍弱但"原生感"更强的路线(Windows/macOS 支持成熟,Linux 不在官方名单);Avalonia 自绘、跨平台最完整,社区活跃,12.x 已经相当稳。选它俩的前提是团队本来就是 C#。

Qt 6 :工业、医疗、嵌入式、需要 10 年长期维护的场景,Qt 依然是第一选择,商业授权能买到确定性。但 LGPLv3 的动态链接对闭源商业应用的合规要求需要法务确认,且 C++ 开发效率摆在那里------前端团队用 Qt,等于放弃已有的一切积累。


三、数据口径与证据强度(重要)

技术文章最怕数字来源不明。我把上面每个数字的来路讲清楚,你自己判断信不信:

数字 怎么来的 证据强度
VS Code 安装包 224 MB curl -IL 官方更新端点,拿到 Content-Length: 235072944(VSCodeUserSetup-x64-1.137.0.exe) 实测,可复现
Notion 安装包 104 MB 同上,跳转链最终 Content-Length: 108831832(Notion Setup 7.33.0) 实测,可复现
Tauri 侧 4.7 MB / 6.5 MB / 13.1 MB 我自己的项目 Release 资产大小,GitHub API 直读字节数 实测,可复现
各大框架最新版本号 crates.io / GitHub Releases / NuGet / 官方 release JSON 一手来源
Electron 系"100--230 MB"区间 上表两个实测点 + Electron 应用普遍分布 中等(不同应用裁剪差异大)
Flutter / MAUI / Avalonia / Qt 体积区间 社区共识区间,未逐项实测 (量级参考,别当精确值)
"Tauri 内存是 Electron 的几分之一" 机制上成立(不捆 Chromium),但具体倍数强依赖应用形态 (我在下文只讲机制,不给倍数)

一句话规矩:装到用户机器上的安装包字节数,我只信 Content-Length 和自己的 Release 资产;倍数关系只讲机制,不讲玄学。


四、为什么我最后选了 Rust + Vue

不是因为它新,而是因为这 5 条对我这套场景是"硬收益"。

1. 分发成本直接降一个数量级

Downloading 224 MB 和 4.7 MB,是两个不同的用户决策。差的不只是带宽,还有:

  • 安装包的更新换代成本(用户每次升级都要重下这么大一坨);
  • 只装在 C 盘不够的机器上的空间焦虑
  • 企业内网、离线分发、U 盘携带的场景(我做了一份 6.4 MB 的绿色版 zip,解压即用)。

我把一整套跨平台产物实测列出来(v1.4.6,GitHub Release 资产字节数):

平台/产物 大小 说明
Windows NSIS .exe 4.68 MB 主力分发格式
Windows .msi 6.51 MB 企业域分发用得着
Windows 绿色版 .zip 6.44 MB 解压即用,数据在程序目录
macOS 通用 .dmg 13.14 MB Intel + Apple Silicon 合一
Linux .deb / .rpm 8.05 MB 依赖系统 webkit2gtk
Linux .AppImage 79.3 MB 见第七节第 4 条,这个数字很真实

顺手打个假:网上说"Tauri 应用都是几 MB",那只对 Windows/macOS 的安装包成立。AppImage 会把依赖链一起打进去,实测 79.3 MB------所以别拿"Tauri 只有 4 MB"去承诺 Linux 用户。

2. 前端资产零迁移,开发体验还是 Vite

这点被严重低估。Tauri 的前端就是一个普通 Web 项目:

ts 复制代码
// package.json ------ 没有任何 Tauri 特有依赖侵入前端
"dependencies": {
  "@tauri-apps/api": "^2",
  "klinecharts": "^10.0.2",
  "naive-ui": "^2.44.1",
  "pinia": "^3.0.4",
  "vue": "^3.5.13"
}

Vite HMR 照常,Vue DevTools 照常,Pinia / Naive UI / KLineChart 全部直接搬。**我在 Web 端攒的组件、样式变量、主题切换方案,一行没重写。**这是 Electron 之外所有框架(Flutter/Qt/MAUI/Avalonia)都给不了的------它们都要求你把 UI 用另一套语言重画一遍。

3. 重活下沉到 Rust,前端只管渲染

这是 Tauri 相比"前端调系统 API"式方案最大的不同:你可以自己决定哪些逻辑放在 Rust 侧。我这套工具里前端只有约 5,200 行(含 Vue/TS/CSS),Rust 侧约 4,300 行,各司其职:

下沉到 Rust 的能力 解决什么问题
reqwest 共享连接池 + 统一超时 前端不做 HTTP,没有 CORS、没有跨域凭据问题
GBK → UTF-8 解码(encoding_rs 行情接口返回 GBK,浏览器 fetch 处理起来很难受
rusqlite(bundled SQLite) 离线缓存行情,重启即有数据,不依赖 Node 原生模块编译
tokio 后台轮询调度器 定时任务不占用 WebView 主线程,窗口卡不掉
多数据源适配与回退 腾讯源挂了自动切新浪源,对前端完全透明

举个真实片段------数据源抽象 + 共享连接池,这是我看重的"工程感":

rust 复制代码
#[async_trait]
pub trait DataSource: Send + Sync {
    fn name(&self) -> &str;
    async fn fetch_quotes(&self, codes: &[String]) -> Result<Vec<Quote>, AppError>;
    async fn fetch_depth(&self, code: &str, market: &str) -> Result<Depth, AppError>;
    async fn fetch_minute_data(&self, code: &str, market: &str) -> Result<Vec<MinuteData>, AppError>;
    // ...
}

// Client::clone() 是浅拷贝:连接池、TLS 配置、超时全部共享
static SHARED_CLIENT: OnceLock<Client> = OnceLock::new();

pub fn shared_client() -> &'static Client {
    SHARED_CLIENT.get_or_init(|| {
        Client::builder()
            .timeout(Duration::from_secs(10))
            .pool_max_idle_per_host(10)
            .build()
            .expect("Failed to build shared reqwest Client")
    })
}

而"主源拿不到数据时静默回退"这种逻辑,写在 Rust 侧只有二十行,且前端完全无感

rust 复制代码
#[tauri::command]
pub async fn get_intraday(
    code: String, market: String,
    manager: State<'_, Arc<DataSourceManager>>,
) -> Result<Vec<MinuteData>, String> {
    // 优先用活跃源;返回空时回退到备用源(如主源对北交所代码无分时数据)
    let active_name = manager.active_name();
    if let Some(source) = manager.get_source(&active_name) {
        if let Ok(data) = source.fetch_minute_data(&code, &market).await {
            if !data.is_empty() { return Ok(data); }
        }
    }
    for (name, source) in manager.all_sources() {
        if name == active_name { continue; }
        match source.fetch_minute_data(&code, &market).await {
            Ok(data) if !data.is_empty() => return Ok(data),
            Ok(_) => {}
            Err(e) => log::warn!("分时回退数据源 {} 失败: {}", name, e),
        }
    }
    Ok(vec![])
}

4. 进程模型干净,安全模型是"白名单式"的

Electron 里渲染进程和 Node 同源,安全全靠你自觉配 contextIsolation / preload;Tauri 里前端根本碰不到 Node 和系统,只有你显式注册的命令能被调用:

rust 复制代码
#[tauri::command]
pub fn get_quotes(cache: State<'_, Arc<QuoteCache>>) -> Vec<Quote> {
    cache.get_all_quotes()
}

配合 tauri.conf.json 里的 CSP,前端连外链脚本都加载不了:

json 复制代码
"security": {
  "csp": "default-src 'self'; img-src 'self' asset: https://raw.githubusercontent.com; \
          style-src 'self' 'unsafe-inline'; script-src 'self'"
}

对一个要处理用户自选股、本地数据库、还要联网的桌面应用来说,"默认没有权限、能力靠显式声明" 比"默认全有权限、靠开发者记得关"要安全得多。

5. 一套前端,将来能编到移动端

Tauri 2 的 IOC 支持意味着这套 Vue 前端理论上可以直接编到 iOS/Android(WebView 换成系统 WebView,Rust 复用)。我个人暂时不做移动端,但**"以后要做时不用重写"这件事,本身就是选型价值**------Flutter 之外,只有 Tauri 给了这条退路。


五、诚实劝退:Rust + Vue 的 5 个代价

写到这里必须泼冷水。如果你只看到上面那些,上线后会很难受。

1. WebView 碎片化是真问题,不是"注意一下"就能过。 Windows 是 WebView2(Chromium,能自更新),macOS 是 WKWebView(跟系统 Safari 版本绑定,官方文档明确说:不再维护的 macOS 版本收不到 WebKit 更新),Linux 是 WebKitGTK(各发行版版本参差,官方参考表里从 2.20 到 2.36 都有)。**你的 CSS/JS 要在 Chromium 和 WebKit 两套引擎上都验,只测 Windows 等于没测。**我用 Playwright 跑测试时也踩过这个坑------Chrome 内核全绿,真机 macOS 上布局就是不一样。

2. 首次运行有 WebView2 依赖。 官方文档的说法是:WebView2 支持 Windows 7 及以上,Windows 11 已预装 ;比 Windows 11 老的系统,Tauri 生成的安装器会负责确保 WebView2 被安装。也就是说:联网环境基本无感,纯离线/内网分发的场景要额外准备一份 WebView2 运行时,别等到客户现场才想起来。

3. Rust 的学习曲线和编译时间要有人买单。 首次全量编译十几分钟起步。团队里必须有人能看懂 Rust 的 async/Send/生命周期错误------报错信息虽然友好,但对没写过 Rust 的同学依然是道墙。如果你的团队全是前端且不打算学 Rust,Tauri 的优势会打折(能用到的大概只有"体积小"这一条)。

4. 生态密度不如 Electron。 Electron 十年的 Stack Overflow 沉淀,Tauri 没有。Tauri 的问题更多靠官方文档 + Discord + GitHub Issue 解决,插件质量也确实参差(官方插件最稳,社区插件要自己读源码确认)。遇到冷门需求时,你花在解决问题上的时间会更多。

5. Linux 分发仍然是脏活。 上面那个 79.3 MB 的 AppImage 就是证据。此外各发行版的 webkit2gtk 版本、Wayland 下的窗口行为、托盘图标实现,都是要单独测的。如果你的用户里 Linux 占比很高,请把这一项工时单独估进去。


六、落地架构:三层切分 + 事件驱动的桥

选型定了,真正的工程问题是前后端边界画在哪。我这套是三层:

arduino 复制代码
┌─────────────────────────────────────────────┐
│ Vue 3 表现层(Pinia + Naive UI + KLineChart)│  ← 只做渲染 + 交互
├─────────────────────────────────────────────┤
│ Tauri IPC 桥(命令 invoke ↑ / 事件 listen ↓)│  ← 22 个命令,职责单一
├─────────────────────────────────────────────┤
│ Rust 数据层(多源适配 / 缓存 / 调度 / SQLite)│  ← 网络、解析、持久化、定时
└─────────────────────────────────────────────┘

6.1 关键设计:后端主动推,而不是前端轮询

新手最容易犯的错是"前端定时 invoke 拉数据"。我这边反过来:Rust 侧调度器按自适应频率抓数据 → 更新缓存 → 向前端发事件,前端只订阅:

ts 复制代码
// stores/quote.ts ------ 事件驱动,前端一次 invoke 都不发(除了启动补快照)
unlistenQuotes = await listen<Quote[]>('quotes-updated', (event) => {
  const map = new Map<string, Quote>()
  for (const q of event.payload) map.set(`${q.market}:${q.code}`, q)
  quotes.value = map
  lastUpdate.value = Date.now()
})

好处很直接:推送频率由后端统一决定(交易时段 2s、午休 10s、休市 30s),前端不需要知道什么时候该快、什么时候该慢;而且整批数据一次到位,不会有 N 个组件各拉各的。

6.2 一个必踩的竞态:事件比监听器先到

后端调度器在窗口还没渲染完时就可能推了第一批数据,前端 listener 是后注册的------首屏空白就是这么来的 。修复方式很简单:订阅完事件,再 invoke 一次拉当前缓存快照兜底(代码注释我当时写得很清楚):

ts 复制代码
// Pull initial state from the backend cache to cover the race window
// where the scheduler already emitted data before listeners were ready.
const [cachedQuotes, cachedIndices] = await Promise.all([
  invoke<Quote[]>('get_quotes'),
  invoke<IndexQuote[]>('get_indices'),
])

6.3 让 CPU 降温:三态自适应轮询

行情工具最怕"死循环定时器"。A 股一天只有 4 小时在交易,节假日更是一动不动------如果每 2 秒抓一次数据、抓 8 小时,你的程序就是个电老虎。我写了个三态状态机(Rust 侧):

rust 复制代码
enum PollingState {
    Probing { remaining: u32 },        // 开盘探测:每 2s 抓,看价格动不动
    Normal  { unchanged_streak: u32 }, // 正常交易:按会话频率
    Idle,                              // 判定休市(节假日):降到 30s
}

impl PollingState {
    fn update(&mut self, prices_changed: bool, session: MarketSession) -> u64 {
        match self {
            Self::Probing { remaining } => {
                if prices_changed {
                    *self = Self::Normal { unchanged_streak: 0 };
                    return session.recommended_interval();
                }
                *remaining -= 1;
                if *remaining == 0 { *self = Self::Idle; return IDLE_INTERVAL; }
                PROBE_INTERVAL
            }
            Self::Normal { unchanged_streak } => {
                if prices_changed { *unchanged_streak = 0; }
                else {
                    *unchanged_streak += 1;
                    if *unchanged_streak >= STREAK_THRESHOLD { // 连续 10 次无变化
                        *self = Self::Idle; return IDLE_INTERVAL;
                    }
                }
                session.recommended_interval()
            }
            Self::Idle => {
                if prices_changed { *self = Self::Normal { unchanged_streak: 0 }; }
                IDLE_INTERVAL
            }
        }
    }
}

逻辑很土但很有效:开盘先探三次,价格动了 = 真开市;连续 10 次价格没动 = 大概率节假日,降频到 30 秒 。一旦发现价格又动了,立刻回到正常频率。MarketSession 用固定 UTC+8 计算,不依赖用户机器的本地时区------这行代码是给海外出差用户留的。

6.4 多窗口 / 托盘:Tauri 的配置式多窗口

那个"可拖到屏幕任意角落的浮动行情条"就是一个独立窗口 + 独立前端入口:

json 复制代码
{
  "label": "ticker",
  "url": "ticker.html",
  "width": 230, "height": 38,
  "decorations": false,
  "alwaysOnTop": true,
  "skipTaskbar": true,
  "resizable": false,
  "focus": false
}

这里有个很深的坑:Tauri 的 skipTaskbar 走的是 COM 的 ITaskbarList::DeleteTabWindows 资源管理器重启后,窗口会重新出现在任务栏上 。解法是直接给窗口加 WS_EX_TOOLWINDOW 扩展样式(Rust 侧调 Win32 API):

rust 复制代码
const GWL_EXSTYLE: i32 = -20;
const WS_EX_TOOLWINDOW: isize = 0x80;

pub unsafe fn set_tool_window(hwnd: isize) {
    let ex_style = GetWindowLongPtrW(hwnd as HWND, GWL_EXSTYLE);
    if ex_style & WS_EX_TOOLWINDOW != 0 { return; } // 幂等
    SetWindowLongPtrW(hwnd as HWND, GWL_EXSTYLE, ex_style | WS_EX_TOOLWINDOW);
    SetWindowPos(hwnd as HWND, std::ptr::null_mut(), 0, 0, 0, 0,
        SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}

这一段在 Electron 里要写原生模块,在 Tauri 里就是一个 #[cfg(target_os = "windows")] 模块 + 几个 extern 函数------宿主是 Rust 的另一个好处。

6.5 发版:一个 tag 出全平台包

打包矩阵、签名、自动更新、CHANGELOG 抽正文、绿色版 zip------这套我在上一篇里写透了,这里只给结果:推一个 tag,Windows/macOS/Linux 三平台产物 + 自动更新元数据全自动产出,0 服务器 0 成本。


七、踩坑清单(收藏这张表)

症状 真因 解法
首屏空白,第二次打开又正常 后端事件先于前端 listener 注册 订阅后再 invoke 拉一次缓存快照兜底
macOS 上滚动条/圆角/字体和 Windows 不一致 WebView2(Chromium) vs WKWebView(WebKit) 双引擎真机验证,别只跑 Chromium 测试
老系统启动白屏 WebView2 运行时缺失 安装器引导安装;离线场景预置运行时
CPU 一直 10% 以上下不来 定时器无脑高频轮询 三态自适应轮询(开盘探测/正常/降频)
Linux 包比 Windows 大十几倍 AppImage 把 webkit2gtk 依赖链打进去了 接受它,或优先发 .deb/.rpm
资源管理器重启后迷你条跑回任务栏 skipTaskbar 的 COM 方案不持久 WS_EX_TOOLWINDOW 扩展样式
打包后白屏,dev 正常 CSP / 资源路径 / frontendDist 配错 tauri build --debug 打开控制台查
更新静默失败、日志没报错 updater 的坑(有三类) 见我的上一篇:《0 元成本搞定桌面端自动更新》
安装包还能更小吗 默认 Cargo profile 没优化 官方文档给了 profile:lto=trueopt-level="s"panic="abort"strip=true,另可开 build.removeUnusedCommands 砍掉未授权命令

八、这套组合在我项目上的实际结果

上面每一节都不是纸上推演。我把它用在开源项目 QuantDesktop(一个 A 股盯盘工具)上了,数据都是真的:

工程规模 :前端 5,226 行(Vue 3 + TS + CSS,41 个文件)+ Rust 4,314 行(17 个文件),22 个 #[tauri::command] 就是全部的前后端契约。对个人项目来说,这个体量一个月内做到 1.4.6 版本、覆盖三平台,靠的就是"前端资产零迁移 + Rust 干重活"。

几个实现层面的选择

  • 行情能力:多数据源(腾讯/新浪)适配 + 自动回退,GBK 统一解码,量额归一化(手→股、万元→元);
  • 调度:三态自适应轮询 + 交易时段判定(固定 UTC+8,节假日靠"价格静止"识别);
  • 持久化:SQLite 缓存报价与自选,重启即有数据;
  • 窗口:主窗口 + 无边框置顶迷你行情条(双前端入口)+ 系统托盘;
  • 分发:NSIS/MSI/dmg/deb/rpm/AppImage + 绿色版 zip,内置静默自动更新(交易时段不弹打扰弹窗)。

项目地址(Windows / macOS / Linux 安装包与绿色版都有): github.com/Leaderxin/q...

前两篇姊妹篇,正好把这条链路的上下游补齐了:


九、一句话选型(抄走就能用)

bash 复制代码
你的团队是「前端 + 愿意碰 Rust」?
  ├─ 是 → 体积/内存是卖点(下载站分发、内网、绿色版)?
  │        ├─ 是 → Tauri 2 + Rust + Vue/React  ✅(本文这套)
  │        └─ 否 → 需要 Node 全生态/复杂系统集成?
  │                 ├─ 是 → Electron(成熟压倒一切)
  │                 └─ 否 → Tauri 2(省下的体积迟早有人感谢你)
  └─ 否 → 主力语言是?
           ├─ Go        → Wails(等 v3 转正,或先用 v2)
           ├─ C#        → Avalonia 12(跨平台最全)/ MAUI(要原生控件观感)
           ├─ Dart/移动端一起做 → Flutter Desktop
           └─ C++/工业长期维护 → Qt 6(提前理清 LGPL 合规)

再补三句经验:

  1. 能用 Web 技术就别重画 UI------除非你的产品卖点就是原生观感或绘制性能;
  2. 安装包体积是产品指标,不是技术洁癖------4.7 MB 和 224 MB 的转化率差异,做过分发的人都懂;
  3. 选框架时把"验证成本"算进工时:系统 WebView 方案省的是分发,花的是多引擎测试;打包 Chromium 方案省的是测试,花的是分发。

写在最后

桌面端框架之争,最后往往不是技术之争,而是你愿意把成本放在哪的选择:放在用户的硬盘和带宽上(Electron),放在自己的兼容性验证上(Tauri/Wails),还是放在重写 UI 的人力上(Flutter/Qt/MAUI/Avalonia)。

我这套选择的理由很简单:我的用户在国内、用普通家宽下载、很多人只想「下载下来就能用」,那我就必须把体积和启动体验做到极致------剩下的验证成本,我自己扛。

如果你也在纠结桌面端选型,欢迎在评论区告诉我你的场景(团队语言、用户地域、要不要移动端),我可以帮你把这张账算得更细。代码和安装包都在 GitHub 上,装了觉得有用的话,给个 Star 就是最大的支持。

本文所有体积数据均为 2026-09-14 实测:Electron 系安装包取自官方更新端点返回的 Content-Length,Tauri 侧取自项目 GitHub Release 资产字节数;框架版本号取自 crates.io / GitHub Releases / NuGet / 官方 release metadata。区间值为社区共识,已在第三节标注证据强度。

相关推荐
弈栈录3 小时前
LangChain Agent 实战:快速构建可调用工具的智能体
python·架构
Solara3 小时前
中文字体子集化实录:3.2MB → 200KB 的完整脚本、踩坑清单与三条反直觉经验
前端·css·架构
天远API4 小时前
零信任架构实战:基于天远全能消金报告构建自动化消费分期网关
运维·人工智能·架构·自动化
雪碧聊技术4 小时前
DeepSeek V4.1 Flash 架构全面拆解——552B MoE、KV Cache 压缩至 1/4、API 价格腰斩背后的技术账
架构
代码方舟5 小时前
零信任架构实战:基于天远全能消金报告构建自动化信用合规网关
运维·人工智能·架构·自动化
隔窗听雨眠5 小时前
Databend Cloud化简大数据架构完全指南:从多组件堆叠到两组件架构的实践路径
大数据·架构
2601_962218476 小时前
万象生鲜系统协议价底层架构实现生鲜企业报价业务数字化管理
大数据·运维·微服务·云原生·架构
wzdark6 小时前
多核架构下算法并行化的瓶颈与突破点4
算法·架构
Thneonl7 小时前
拆开一道 FDE 面试题,我看到三场十年前的考试
人工智能·架构