前阵子我把一个维护了两年的 Electron 内部工具做了次大迁移,起因很简单:安装包 224MB,内网用户每次升级都要等半天,还有人把安装包放微信里传,直接被拒收。领导问我能不能把体积压下来,我花了两周时间整理了一份横评方案,最终选了 Rust + Vue 的组合,把产物压到了 4.7MB 左右。这篇文章就把这次选型过程、迁移实操、踩过的坑和最终结论完整写出来。
1. 224MB 的“臃肿”不该由用户买单:一个内部工具的迁移起因
1.1 Electron 工具的真实体积拆解
先说我们这个内部工具的背景:一个给运营用的数据报表桌面端,功能并不复杂,主要就是登录、拉接口、展示表格和图表、导出一个 Excel,附带一个简单的更新提示。当初图快,用了 Electron + Vue 2 + Element UI,开发确实快,但从没想过安装包体积这个问题。
有一天我看了一下打包产物目录,224MB,拆开看大致是这么个构成:
- Electron 框架本体(含 Chromium 内核):约 160MB
- node_modules 里各种依赖:约 35MB
- 前端构建产物(JS/CSS/图片等):约 20MB
- 其他资源、图标、初始化脚本:约 9MB
这就是 Electron 方案的本质问题:不管你写多少业务代码,Chromium 和 Node.js 这套运行时的底子是省不掉的。这个工具业务代码可能只有几百 KB,但要背着 220 多兆的浏览器引擎到处跑。对开发者来说这无所谓,但对用户来说,下载慢、占磁盘、内存占用高,这些都是实打实的体验成本。
1.2 为什么我当时选了 Electron,以及现在为什么要换代
选 Electron 不丢人,它是目前生态最成熟的跨平台桌面方案之一,VS Code、Slack、Discord 都在用它。当时选它,核心原因是团队只有前端,没有原生开发经验,Electron 能让我们用一套 JavaScript 代码直接搞定桌面端,成本最低。
但这次迁移不是拍脑袋,而是三个具体痛点让我下决心换:
- 体积敏感:内网分发平台限制单文件 100MB,Electron 的 224MB 被迫走网盘,体验很割裂。
- 内存占用高:用户电脑配置普遍不高,这个工具常驻内存经常到 400MB 以上,展开几十个标签页的 Chromium 很吃资源。
- 启动速度:机械硬盘上冷启动经常要 4~5 秒,用户已经不止一次提过“这个工具怎么这么慢”。
这三个问题都属于 Electron 架构层面的“原罪”,靠优化代码解决不了根本。所以我把视角放到更底层:能不能换一个不打包浏览器的方案?
2. 六种跨平台桌面方案的核心维度对比:体积、生态与开发效率
2.1 参评方案的选型范围
我把市面上主流的跨平台桌面方案都拉了一遍,最终筛出六个有代表性的,按技术路线分三类:WebView 派、原生 UI 派、跨平台运行时派。
- Electron:基准方案,Chromium + Node.js。
- Tauri(Rust):核心思路是调用系统 WebView,后端用 Rust。
- Wails(Go):同样调 WebView,后端用 Go。
- Qt(C++/QML):经典原生方案,但可以通过 QML 做出接近前端的开发体验。
- Flutter Desktop:自绘引擎,不用系统 WebView,也不打包浏览器。
- pywebview(Python):轻量级 Python 库,顾名思义就是 Python + 系统 WebView。
2.2 核心维度逐项对比
对比不能只看体积,我从工程落地的角度选了六个维度:打包体积、内存占用、开发语言、生态成熟度、UI 还原度、上手成本。结果如下表:
| 方案 | 打包体积(典型最小) | 内存占用 | 后端语言 | UI 技术 | 生态成熟度 | 适合团队 |
|---|---|---|---|---|---|---|
| Electron | 150-250MB | 高 | JavaScript | HTML/CSS/JS | 极高 | 纯前端团队 |
| Tauri | 4-10MB | 低 | Rust | HTML/CSS/JS | 中高 | 前端 + 愿意学 Rust |
| Wails | 8-15MB | 低 | Go | HTML/CSS/JS | 中 | 前端 + Go 团队 |
| Qt | 30-80MB | 中低 | C++ | QML/Widgets | 高 | 原生开发团队 |
| Flutter Desktop | 20-40MB | 中 | Dart | Flutter Widgets | 中 | Flutter 团队 |
| pywebview | 10-30MB | 中 | Python | HTML/CSS/JS | 中低 | Python 工具链团队 |
体积方面,Tauri 和 Wails 这类基于系统 WebView 的方案优势明显;但要注意,这只是“安装包体积”,系统 WebView 本身是操作系统自带的,Windows 上是 WebView2,macOS 上是 WKWebView,Linux 上是 webkit2gtk,这部分资源不占你的安装包。
2.3 我这轮横评后划掉的选项
排掉方案是有明确理由的,不是它们不好,而是不适合这个项目。
- Electron:继续保留作为对照组,不优先考虑。
- Wails:Go 后端很香,但项目里没有人长期写 Go,且 Wails 在某些 Linux 发行版上的 WebView 兼容性踩坑记录比 Tauri 多。
- Qt:C++ 这门语言对前端团队陡峭,维护成本太高,而且做数据报表界面不如 HTML 灵活。
- Flutter Desktop:桌面端还在完善期,和 Vue 生态完全不搭,意味着前端资源没法复用。
- pywebview:Python 做桌面小程序确实快,但对于需要内嵌数据缓存、做系统托盘、深度操作文件系统的场景,Python 打包和分发也比较折腾。
最终胜出的是Tauri,理由后面实操部分展开。这里先给一个直觉类比:Electron 相当于你开一家餐厅,为了一个顾客专门背了一套完整的中央厨房设备;Tauri 相当于让顾客用自己家的厨房,你只负责带食材和调料。设备不用你带,体积自然小。
3. Rust + Vue 的 Tauri 方案为何能打出 4.7MB 的安装包
3.1 Tauri 的体积秘密:复用系统 WebView + Rust 原生二进制
Tauri 最关键的设计是不打包浏览器内核。在 Windows 上它调用 WebView2 运行时(Edge Chromium 内核),macOS 上调用 WKWebView,Linux 上调用 webkit2gtk。这些 WebView 都是系统级组件,安装包不需要把它们塞进去。
应用本体由一个 Rust 编译出的原生可执行文件和前端构建产物组成。我的项目实际结果是:
- Rust 可执行文件:约 3.8MB(编译优化 + strip 后)
- Vue 3 构建产物(JS/CSS/HTML/图片等):约 900KB
- 压缩包打包后:4.7MB
注意这个数字是 Linux 下用 xz 压缩后的 tar 包,Windows 下的 NSIS 安装包会带上 WebView2 引导安装程序(约 1-2MB),最终 7MB 左右,也比 Electron 小一个数量级。
3.2 前端为什么可以是 Vue:Vite + Vue 3 构建出静态资源
Tauri 对前端框架没有限制,Vue、React、Svelte 都行。它的原理是在本地起一个静态资源服务(生产环境可用 tauri://localhost 协议加载内嵌资源),WebView 加载的是你构建出的 HTML/JS/CSS。
所以我的前端技术栈基本原封不动地从 Electron 搬了过来:
- Vue 3 + Vue Router + Pinia
- Vite 作为构建工具
- Element Plus 组件库
- ECharts 画报表图表
迁移成本主要是适配数据请求方式:原来用 axios 直接发 HTTP,现在可以通过 Tauri 的 Rust 后端来发请求,也可以继续在 WebView 里发,看业务需要。
3.3 Rust 后端的定位:把系统级能力从 Node.js 手里接过来
Electron 里我们用 Node.js 做文件系统操作、对话框、通知、自动更新。Tauri 里这些由 Rust 端提供:
- 文件读写:用 Rust 标准库 std::fs,或者用 tauri-plugin-fs
- 系统对话框:tauri-plugin-dialog
- 系统通知:tauri-plugin-notification
- 打开外部链接:tauri-plugin-opener
- 定时任务:Rust 的 tokio 或 std::thread
一开始团队担心 Rust 学不会,但实际接触下来,写几个 command 函数的门槛并不高:函数加了 #[tauri::command] 宏,前端用 invoke 调用,跟 Electron 的 ipcMain.handle 长得几乎一模一样,只是换了一套语法。
3.4 4.7MB 是怎么优化出来的:Cargo 配置里的关键参数
不是装完 Tauri 就有 4.7MB,需要做针对性优化。核心在 Cargo.toml 的 release profile 里:
[profile.release] panic = "abort" codegen-units = 1 lto = true opt-level = "z" strip = true逐项解释一下:
opt-level = "z":优先优化体积而不是运行速度,对工具类应用来说性能足够。lto = true:开启链接时优化,能让 Rust 的泛型代码在链接阶段进一步去重,体积能再降百分之十几。codegen-units = 1:让编译器在单线程下生成代码,虽然编译时间变长,但能配合 LTO 做更多优化。panic = "abort":发生 panic 时直接终止而不是展开栈,减少编译产物里的 unwinding 代码。strip = true:去掉符号表,编译产物里不带调试信息。
前端侧也要配合:
- 关闭 Vite 的 sourcemap
- 去掉没用到的组件依赖
- 图片资源做压缩
一个很直观的对比:同样一个 “Hello World” 级别应用,默认配置编译出来大约 8MB,做完上面的优化能压到 4MB 左右,再把前端资源压缩进去,4.7MB 是很正常的水平。
4. 迁移实操:把一个 Electron + Vue 2 工程改造成 Tauri + Vue 3
4.1 环境准备与项目初始化
我是在一台 Ubuntu 22.04 上先做验证,然后退回 Windows 10 开发主力机。Tauri 在这两个平台都能良好工作,但要注意 Linux 下的系统依赖:
sudo apt install libwebkit2gtk-4.1-dev \ build-essential \ curl \ wget \ file \ libxdo-dev \ libssl-dev \ libayatana-appindicator3-dev \ librsvg2-devWindows 上主要是装 Rust 工具链(rustup)和 VS Build Tools。Node.js 建议直接用 18 LTS 以上版本,Tauri 2.x 对 Node 18/20 都支持良好。
初始化用官方脚手架:
npm create tauri-app@latest模板选择时我建议选 “Vue + TypeScript”,因为 Tauri 的命令行工具和 API 对 TS 类型支持已经很完善,能减少很多低级错误。生成出来的目录结构大概是:
- src/:Vue 前端代码
- src-tauri/:Rust 后端代码
- src-tauri/tauri.conf.json:核心配置
- src-tauri/Cargo.toml:Rust 依赖
4.2 把 Vue 2 工程升级到 Vue 3 + Vite
这是整个迁移中最耗时的一部分。老工程用的是 Vue 2 + vue-cli,Tauri 的官方模板默认是 Vite,所以不能直接把旧代码拷贝进去,得先升级。
升级路径我走的是:
- 用
vue upgrade工具把 Vue 2 组件过渡到 Vue 3 语法。 - 把 vue-cli 的
public/目录迁移成 Vite 的public/和src/assets/。 - 检查所有生命周期钩子:Vue 2 的
beforeDestroy改成onBeforeUnmount,filters 语法移除。 - 用 Vite 的
server.host配置保证 Tauri dev 模式下能访问前端开发服务器。
这里有个经验:先只迁移核心页面,所有非核心功能(比如设置页、意见反馈)放到后期再处理,避免一次性把工程搞崩。
4.3 配置 tauri.conf.json:identifier 和 devUrl 是高频踩坑点
Tauri 2.x 的配置都在 src-tauri/tauri.conf.json 里,最关键的是这段:
{ "$schema": "https://schema.tauri.app/config/2", "productName": "data-report", "version": "1.0.0", "identifier": "com.example.datareport", "build": { "beforeDevCommand": "npm run dev", "devUrl": "http://localhost:5173", "beforeBuildCommand": "npm run build", "frontendDist": "../dist" }, "app": { "windows": [ { "title": "数据报表", "width": 1280, "height": 800, "minWidth": 1024, "minHeight": 700 } ], "security": { "csp": null } }, "bundle": { "active": true, "targets": ["deb", "appimage", "msi", "nsis"] } }特别提醒identifier这个字段:Tauri 2.x 要求 bundle identifier 不能使用默认的com.tauri.dev,否则打包时直接报错。另外devUrl必须和 Vite 的开发端口一致,否则开发模式白屏,这个我折腾了半天才反应过来。
4.4 把 Electron 的 IPC 调用改造成 Tauri 的 invoke
Electron 是ipcRenderer.invoke('export-excel', data)+ipcMain.handle('export-excel', ...)的组合。Tauri 换成这样:
前端(src/api/export.ts):
import { invoke } from '@tauri-apps/api/core'; export async function exportExcel(data: Record<string, unknown>) { return await invoke('export_excel', { data }); }Rust 后端(src-tauri/src/lib.rs):
use tauri::State; #[tauri::command] fn export_excel(data: serde_json::Value) -> Result<String, String> { // 在这里写文件导出逻辑 match crate::excel::export(&data) { Ok(path) => Ok(path), Err(e) => Err(e.to_string()), } } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![export_excel]) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_opener::init()) .run(tauri::generate_context!()) .expect("error while running tauri application"); }注意invoke的参数命名:Rust 函数参数是data,前端 invoke 传{ data },Tauri 会自动做 camelCase 转 snake_case 的映射,不需要手动处理。但如果你 Rust 侧用#[tauri::command(rename_all = "camelCase")]也是可以的,看项目风格。
4.5 打包构建与体积验证
开发调试没问题后,打包就一条命令:
npm run tauri build首次编译很慢,Rust 要拉取并编译几百个 crate,项目依赖比较多的情况下 5 到 10 分钟很正常,第二次开始有缓存会快很多。打包完成后,看产物尺寸:
ls -lh src-tauri/target/release/bundle/appimage/data-report_1.0.0_amd64.AppImage实测第一次打包默认配置是 8.6MB,按照 Cargo.toml 里的 release profile 优化配置重新编译后,最终 AppImage 稳定在 5.1MB,tar.xz 压缩包 4.7MB。Windows 的 NSIS 安装包 7.2MB,比 Linux 稍大,原因是 NSIS 安装器本身和 WebView2 引导程序加起来约 2MB。
5. 迁移路上最典型的五个坑,以及对应的排查思路
5.1 Linux 打包时 fpm 报错:不是代码问题,是缺依赖
这是热词里出现频率很高的一个问题,我也踩了。打 deb 包时 Tauri 内部会调用 fpm 工具做打包,报错信息很抽象:
error: failed to bundle project: error running fpm failed to read output排查过程:先确认 fpm 是否安装,然后看 Tauri 的系统依赖清单,发现少了ruby和gem。Tauri 2.x 在 Linux 下打 deb 包依赖 fpm,而 fpm 是 Ruby 生态的工具链。装上之后就好了:
sudo apt install ruby-full sudo gem install fpm这个问题本质上不是 Tauri 的 bug,而是 Linux 环境差异大、打包工具链不统一。我后来在 CI 里都会提前装上这些工具,避免每次打新包都有环境问题。
5.2 Windows 精简系统里 WebView2 缺失:安装包需要带引导器
项目用户里有不少人的 Windows 10 是精简过的,WebView2 运行时被裁剪掉了。Tauri 应用一启动就是白屏,没有任何提示。
解决方案有两个:
- 在 bundle 配置里启用 WebView2 引导安装(Windows 安装包默认会携带)。
- 在应用启动时检测 WebView2 是否可用,不可用则弹出提示并跳转官方安装地址。
检测方式可以写一个简单的 Rust command:
#[tauri::command] fn check_webview2() -> bool { // 检查注册表或系统目录 // Windows 上可通过查询 WebView2 Runtime 的注册表键值判断 true // 简化示意 }这个坑在 Electron 时代完全不存在,因为 Electron 自带 Chromium。用 Tauri 后,目标机器的 WebView2 是个外部运行时,不能被默认依赖。我当时的处理是在安装说明里明确加一句“需要 WebView2 运行时”,同时安装包选择带引导器的模式。
5.3 localStorage 和 IndexedDB 在 WebView 里“不持久”:权限上下文问题
Tauri 的 WebView 默认允许使用 localStorage 和 IndexedDB,但有一个容易被忽略的点:WebView 的持久化存储是绑定应用标识的,重新编译时如果改了 identifier,存储分区会变,看起来数据就丢了。
我在迁移时把 identifier 从默认值改了,结果开发调试阶段登录态天天掉,排查了很久才发现是分区变化。另外,macOS 上的 WKWebView 对 localStorage 的清理策略和 Windows 不太一样,某些系统版本升级后也会清空 WebView 存储。
如果你的应用对本地缓存有强需求,建议不要依赖 WebView 的 localStorage,而是通过 Rust 后端写文件到应用数据目录。这样对数据控制力更强,也不受 WebView 清理策略影响。
5.4 Node 生态的原生依赖没有 Rust 替代品:先列依赖清单再动手
Electron 工程里用了一些 Node 原生模块,比如用于解析 Excel 的 node-xlsx、用于压缩的 adm-zip。迁移到 Tauri 后,这些 JS 库不能在 Rust 端直接跑。
处理路径有两种:
- 前端继续用 JS 库:如果只是纯解析/生成文件,可以把逻辑保留在前端,用 WebView 的 JS 能力完成,只是不能调用 Node API。
- Rust 端找对应 crate:解析 Excel 用
rust_xlsxwriter或calamine,压缩用zipcrate。
我的建议是先画一张依赖清单,把 Electron 里所有require('node:*')的地方列出来,逐项确认用什么方案替代。这步别看简单,漏一项就会在打包后的某个功能上突然报错,排查成本更高。
5.5 文件下载和默认保存目录的差异:不能简单写死路径
Electron 时代,app.getPath('downloads')可以拿到系统下载目录。Tauri 里这个能力由 Rust 端提供,更好的是用tauri-plugin-dialog让用户选择保存位置,而不是默认下载到固定目录。
我遇到的实际问题是:WebView 里点击链接会直接在当前窗口打开而不是下载,因为 Tauri 默认不接管下载行为。要拦截下载,通常是在前端用锚点拦截 + Rust 后端处理:
#[tauri::command] fn download_file(url: String, save_dir: String) -> Result<String, String> { // 用 reqwest 下载到 save_dir Ok("download finished".into()) }前端拿到文件后,再通过dialog插件让用户选择保存位置,这样比 Electron 的webContents.downloadURL更明确,也不会出现下载了一半用户找不到文件的情况。
6. 方案选型不是选最好而是选边界:六种方案的适用场景
6.1 什么时候继续用 Electron:别为了换而换
做完这次迁移,我对 Electron 的态度反而更客观了。如果你遇到下面几类情况,继续留在 Electron 完全没问题:
- 重度依赖 Chrome 特性:比如要用 WebRTC、Service Worker、PWA 能力,这类 WebView 支持可能不完整。
- 追求生态和排错效率:Electron 的文档、社区排错方案、第三方库丰富程度,短期还是 Tauri 比不上的。
- 团队没有 Rust 人才储备,且无法接受学习成本:服务端接口已经很成熟,前端写桌面端,Electron 上手效率确实快。
说白了,体积和内存不是衡量桌面框架的唯一标准,Electron 的可靠性已经被大量生产环境验证过了。
6.2 什么时候选 Tauri:这轮横评后的明确判断
Tauri 适合这几类场景:
- 体积敏感型产品:个人工具、企业内部工具、需快速分发的应用,4-10MB 和 200MB 的体验差异是质的。
- 已有 Web 前端资产:团队有 Vue/React 工程师,想用现有代码降低成本。
- 需要低内存占用:Rust 后端 + 系统 WebView 的常驻内存,比 Chromium 内核低一半以上,我的工具实测从 420MB 降到 180MB。
- 有系统能力需求:文件操作、系统托盘、自定义协议、开机自启等,Rust 都能搞定。
6.3 其他四个方案各自的主场
这轮对比后,我给同事的建议是:
- 团队是 Go 背景,优先看 Wails,思路完全一致,只是后端语言换成了 Go,体积略比 Tauri 大点但也在可接受范围。
- 需要统一移动端和桌面端代码,且团队熟悉 Dart,考虑 Flutter Desktop。
- 要做高性能图形处理、工业控制这类桌面软件,老老实实用 Qt。
- 只想给 Python 脚本套个壳,内部工具完全够用,用 pywebview。
6.4 决策框架:一张评估清单
最后给你一张我在选型时用的评估清单,照着打一遍分数就清楚了:
| 维度 | 权重建议 | Electron | Tauri | Wails | Qt | Flutter Desktop | pywebview |
|---|---|---|---|---|---|---|---|
| 安装包体积 | 20% | 2 | 10 | 8 | 6 | 5 | 5 |
| 运行时内存 | 15% | 3 | 9 | 9 | 8 | 6 | 6 |
| 开发效率 | 25% | 9 | 6 | 7 | 5 | 6 | 8 |
| 生态成熟度 | 20% | 10 | 7 | 6 | 9 | 6 | 5 |
| 团队匹配度 | 20% | 10 | 6 | 7 | 5 | 6 | 8 |
权重按自己项目的情况调整,比如你团队全是前端、没有原生经验,那 Tauri 的团队匹配度分数可能要再压低一点;如果极客导向、员工学习意愿强,Tauri 的分数还能再往上提。
7. 附加价值:Vue 生态在 Tauri 桌面端的流媒体播放场景
7.1 从 m3u8 播放到桌面端播放器的轻量方案
这次迁移还顺带验证了一个场景:用 Vue + Tauri 做桌面端播放器。网上搜“vue 播放 m3u8”的开发者不少,说明视频点播、直播在桌面端需求很大,常见的 m3u8 播放方案就是 hls.js 或 video.js 加 hls 插件。这类方案在 Web 页面里能用,在 Tauri 里也完全能复用。
我自己做了一个小验证:把原来的浏览器播放页面直接嵌进 Tauri,播放一个 m3u8 测试流,表现很稳定,首帧加载速度比 Chrome 里还快一点,因为 Tauri 的 WebView 少了一层浏览器扩展和其他标签页抢占资源的过程。
前端部分的核心代码大致是:
<video id="player" controls autoplay></video>import Hls from 'hls.js'; const video = document.getElementById('player') as HTMLVideoElement; if (Hls.isSupported()) { const hls = new Hls(); hls.loadSource('https://example.com/live/stream.m3u8'); hls.attachMedia(video); } else if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = 'https://example.com/live/stream.m3u8'; }这段逻辑在 Chrome、Edge、Tauri 里都能跑。唯一要注意的是跨域问题,如果是私有流地址,可以像我们在 Electron 时代做代理一样,用 Rust 侧写一个本地代理来转发请求,绕开 WebView 的 CORS 限制。Rust 的reqwest库写个几十行的代理很轻松。
7.2 桌面端播放器的额外收益:结合 Rust 做本地缓存和切片预取
桌面端和浏览器端最大的区别在于:桌面端可以更方便地管理本地文件。播放器场景下,可以用 Rust 把远端 m3u8 切片预取到本地缓存目录,下次播放同一个视频时直接读本地文件,卡顿率大幅下降。
我做了个简单的缓存逻辑:
#[tauri::command] async fn prefetch_segments(playlist_url: String, cache_dir: String) -> Result<String, String> { // 解析 m3u8, 获取 segment 列表 // 逐个下载到 cache_dir // 返回缓存后播放地址 Ok(format!("cached playlist: {}", playlist_url)) }这个能力在 Electron 里也能做,但 Node.js 的并发下载控制、文件锁、缓存清理做起来不如 Rust 顺手,而且 Rust 写出来的代码更不容易让 CPU 和内存飙升。如果你的目标平台是低配 Windows 机器,这个优势会被放大。
7.3 一个结论:Vue 生态不会因为换掉 Electron 而废掉
很多人担心从 Electron 迁到 Tauri 等于把前端生态推倒重来。实际迁移完你会发现,Vue 组件、状态管理、路由、UI 组件库、图表库全部照常使用,只是“跨语言 IPC”从 Node.js 换成了 Rust。前端团队的核心技能没有贬值,只是多了一个可以触达系统底层能力的“外挂”。
这个观察也让我更坚定地把工具类的桌面应用优先考虑 Tauri,而不是把代码硬塞进一个看起来很全能的 Chromium 里。
最后再分享两个小技巧
第一个是用cargo bloat分析 Rust 二进制的体积构成。虽然我们做的是应用不是库,但在压体积时能清晰看到哪些 crate 占了大量空间,针对性地精简依赖比盲目改编译参数有效得多。
第二个是把strip和opt-level的改动单独放在一个 profile 里,不要影响 debug 构建。比如用[profile.release]单独配置生产构建,开发时用dev模式快速编译,就能兼顾开发体验和发布体积。
另外,如果你也准备迁一个老 Electron 项目,我的建议是别一次性做全平台迁移,先在 Linux 或 Windows 上跑通主流程,确认体积和功能符合预期,再扩展到 macOS。这样容错率高,团队心理压力也小。迁移本身不困难,难的是那颗“感觉要学一门新语言”的恐惧,其实写两周你会发现,Rust 在这里只是给 Vue 打工的工具人,根本没你想象的那么深刻。