1. 为什么 Tauri 2.0 的升级不是“改个版本号”那么简单
Tauri 2.0 的变化核心有三类:
- 配置体系大重构
tauri.conf.json的结构变化非常大:顶层字段搬家、tauri键改名为app、allowlist 被移除、updater/cli 等移动到插件体系。 - API 全面插件化
以前@tauri-apps/api里很多模块(fs/http/shell/notification…)属于“内置模块”,2.0 里这些大多变成插件(Rust 侧 + JS 侧都要装)。 - 权限系统(Capabilities)取代 allowlist
v1 的 allowlist 被替换为更精细的 ACL 模型:可以按窗口、域名、命令、scope 精确放行/拒绝,并且通过src-tauri/capabilities/下的能力文件生效。
这意味着:升级并不难,但必须“按模块拆解迁移”,否则就会出现典型的“编译过了但功能没了”。
2. 升级前的准备清单(强烈建议先做)
- 锁定当前可工作的 v1 基线
打一个 tag:v1-stable
确保你能随时回滚。 - 清点你用到的 v1 API/功能点
至少列出这些关键项(后面会对应到插件迁移):
- fs / path / http / shell / notification / clipboard / updater / process / os / cli / global-shortcut
- tray / menu
- 自定义协议(asset protocol、pattern、scope)
- allowlist(尤其是 sidecar / fs scope / shell open / process command)
- 清点你当前的配置文件(tauri.conf.json)
把老结构保存一份,迁移时你会频繁对照字段搬家。
3. 移动端准备:把 crate 变成“可共享库”
如果你的目标包含移动端(Android/iOS),2.0 的移动端接口要求项目能输出共享库,所以需要做这些结构调整。
3.1 修改 Cargo.toml:输出库产物
src-tauri/Cargo.toml 追加:
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
含义很直白:同一套 Rust 入口需要同时服务桌面端可执行文件和移动端的库载入。
3.2 入口文件拆分:main.rs 变薄,lib.rs 变“通用入口”
- 把
src-tauri/src/main.rs重命名为src-tauri/src/lib.rs - 把
main()改成通用的run(),并加上移动端入口宏:
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
// your code here
}
tauri::mobile_entry_point 会让这个入口在移动端以正确方式被调用。
3.3 重建 main.rs:桌面端只负责调用 run()
重新创建 src-tauri/src/main.rs:
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
app_lib::run();
}
迁移到这里,你就完成了“同一份 Rust 逻辑,多端复用”的基础改造。
4. 自动迁移:migrate 命令能省力,但不能偷懒
官方给的警告很重要:migrate 不是替代指南,它只是帮你自动做“机械改动”,真正的功能迁移(插件、权限、业务代码)仍然要你手动完成。
4.1 安装 v2 CLI 并运行 migrate
cargo install tauri-cli --version "^2.0.0" --locked
cargo tauri migrate
它通常会帮你做:
- 部分配置字段的迁移
- 把 v1 allowlist 解析成 capability 文件的“初稿”
但注意:生成出来的 capability 是否符合你真实的窗口/域名/sidecar 场景,需要你自己复核。
5. 配置文件迁移:tauri.conf.json 的字段搬家地图
下面这一段是升级里最容易“看起来没报错,但运行行为变了”的地方。你贴的 Summary of Changes 很全,我把它整理成“迁移动作”。
5.1 顶层结构变化
package > productName和package > version移到顶层package节点整体移除tauri键改名为apptauri > bundle移到顶层bundletauri > bundle > identifier移到顶层(通常与 bundle 标识相关)
5.2 二进制名称不再自动跟随 productName
v1 里经常“产品名改了,生成的可执行文件名也跟着变”。
v2 不会自动改了,你必须加一个 mainBinaryName,并保证它与 productName 对齐(否则打包/运行时可能找不到主程序)。
5.3 allowlist 移除:改成 Permissions / Capabilities
tauri > allowlist被移除
迁移方向:创建src-tauri/capabilities/下的能力文件(migrate 命令会帮你生成雏形),再按插件逐项放行。
5.4 安全相关字段的移动
tauri > allowlist > protocol > assetScope→app > security > assetProtocol > scopetauri > pattern→app > security > patterntauri > windows > fileDropEnabled→app > windows > dragDropEnabledbuild > withGlobalTauri→app > withGlobalTauribuild > distDir→frontendDistbuild > devPath→devUrl
5.5 插件配置位置变化
tauri > cli→plugins > clitauri > updater→plugins > updatertauri > updater > active/dialog等旧字段移除(2.0 updater 行为不同,见后文)tauri > systemTray→app > trayIcon
5.6 bundle 子结构重命名(按 OS 归档)
tauri > bundle > dmg→bundle > macOS > dmgtauri > bundle > deb→bundle > linux > debtauri > bundle > appimage→bundle > linux > appimage- Windows WebView Runtime 字段变化:
bundle > windows > webviewFixedRuntimePath移除 → 用bundle > windows > webviewInstallMode
5.7 Updater 的兼容性坑:createUpdaterArtifacts 与 v1Compatible
- 新增
bundle > createUpdaterArtifacts
如果你之前已经分发过 v1 应用,并且希望用户能“从旧版本升级到新版本”,通常需要设置为v1Compatible(它影响升级包生成方式)。这是“升级后用户收不到更新”的高发原因之一。
6. Rust 侧变化:API 模块被拆成插件,很多熟悉的路径都没了
6.1 Cargo feature 变化
新增:
linux-protocol-body(需要 webkit2gtk 2.40)用于自定义协议 request body 解析
移除/更名(重点):
process-command-api、shell-open-api:都改用tauri-plugin-shellupdater:Updater 变成插件system-tray:改名为tray-iconwindows7-compat:移动到 notification 插件
6.2 Rust crate API 变化(你最可能遇到的报错来源)
以下是典型“编译报错 → 对应迁移方向”:
-
tauri::api::*模块整体移除:每个 API 去对应插件里找tauri::api::dialog→tauri-plugin-dialogtauri::api::http→tauri-plugin-httptauri::api::shell/tauri::api::process::Command→tauri-plugin-shelltauri::updater→tauri-plugin-updatertauri::api::notification→tauri-plugin-notification
-
tauri::api::file:直接用 Rust 的std::fs -
tauri::api::path和tauri::PathResolver:迁移到tauri::Manager::path
示例(path 迁移):
use tauri::{path::BaseDirectory, Manager};
tauri::Builder::default()
.setup(|app| {
let home = app.path().home_dir().expect("failed to get home dir");
let p = app.path().resolve("path/to/something", BaseDirectory::Config)?;
Ok(())
});
6.3 Menu/Tray 重构(muda + 新 builder)
Menu:改到 tauri::menu,大量旧类型移除,换成 Builder 风格(你贴的 MenuBuilder / MenuItemBuilder / SubmenuBuilder 就是迁移模板)。
Tray:SystemTray 全部改名为 TrayIcon(更一致),事件拆成:
on_menu_eventon_tray_icon_event
你可以直接套用你贴的 TrayIconBuilder 示例结构去改。
7. JavaScript 侧变化:@tauri-apps/api “瘦身”,其他都去插件包
v2 的结论非常明确:
@tauri-apps/api只保留:core(原 tauri)、path、event、window/webviewWindow- 其他模块:fs/http/shell/os/process/notification/dialog/clipboard/updater/cli/global-shortcut 全部变成
@tauri-apps/plugin-*
7.1 最常见的一刀:@tauri-apps/api/tauri → @tauri-apps/api/core
import { invoke } from "@tauri-apps/api/tauri"
import { invoke } from "@tauri-apps/api/core"
这一步非常常见,很多项目第一处报错就来自这里。
7.2 插件迁移的统一模式(记住这个模板就够了)
每个插件基本都要做三件事:
- Rust Cargo 加依赖:
tauri-plugin-xxx = "2" - Rust Builder 初始化:
.plugin(tauri_plugin_xxx::init())或 Builder 风格 - 前端 npm/pnpm/yarn 加依赖:
@tauri-apps/plugin-xxx - 前端 import 改到插件
比如 Dialog:
Rust:
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
前端:
import { save } from '@tauri-apps/plugin-dialog';
你贴的 Clipboard/CLI/FS/HTTP/Notification/Process/Shell/Updater/OS/Global Shortcut 都是同样套路。
7.3 FS 插件的 API 改名坑
v1 常见函数重命名如下(你贴的很关键):
createDir→mkdirreadBinaryFile→readFilewriteBinaryFile→writeFileremoveDir/removeFile→removerenameFile→renameDir别名移除 → 用BaseDirectory
如果你迁移后“明明装了插件但类型/函数不存在”,通常就是这里没改干净。
8. 权限系统迁移:Capabilities 取代 allowlist(这是 v2 的安全核心)
v1 allowlist 只是一种“模块级开关”。
v2 Capabilities 是真正的 ACL:可以精确到“允许哪个窗口在什么域名下调用哪个命令,scope 是什么”。
你必须做的事情:
- 在
src-tauri/capabilities/创建能力文件(migrate 会帮你生成初稿) - 按插件逐项配置允许项
- 如果你有 sidecar、remote URL、多窗口(甚至未来 multiwebview),能力文件要同步升级,否则功能会“静默失效”
一句话经验:
迁移成功的标准不是“能编译”,而是“权限文件覆盖了你真实的调用路径”。
9. 事件系统、窗口系统、Windows origin:升级后行为变化最大的三块
9.1 事件系统:emit 默认广播,新增 emit_to / emitTo
emit()现在会发给所有监听器- 新增
emit_to/emitTo:定向发给某个 target listen_global→listen_any- JS 的
event.listen()行为更接近 listen_any(除非你设置 target)
如果你原来依赖“按窗口隔离事件”,升级后要重点检查:有没有出现“别的窗口也收到了消息”。
9.2 Window → WebviewWindow(为 multiwebview 做铺垫)
Rust:
Window改名为WebviewWindowWindowBuilder→WebviewWindowBuilderWindowUrl→WebviewUrlget_window→get_webview_window
JS:
@tauri-apps/api/window改到@tauri-apps/api/webviewWindow
9.3 Windows 生产环境 origin 变了:tauri.localhost
v2 在 Windows 生产环境下,前端文件从 https://tauri.localhost 变成 http://tauri.localhost。直接影响:
- IndexedDB / LocalStorage / Cookies 可能被重置(因为 origin 变了)
解决方案(你贴的官方建议):
- 设置
app > windows > useHttpsScheme为 true
或 Rust 侧用WebviewWindowBuilder::use_https_scheme保持 https 方案。
这是那种“用户升级后发现登录态没了”的典型坑,务必提前处理。
10. Updater 迁移:默认自动弹窗没了,你必须自己做更新流程
v2 移除了“内置的自动检查 + 内置弹窗”。
如果你不手动实现检查与安装,你的用户可能永远不会再收到更新。
迁移路径:
- Rust:
tauri-plugin-updater = "2"并.plugin(tauri_plugin_updater::Builder::new().build()) - JS:
@tauri-apps/plugin-updater,自己调用check(),然后downloadAndInstall(),最后用@tauri-apps/plugin-process的relaunch()重启。
你贴的示例就是标准实现模板。
11. 环境变量改名:CI/CD、签名、打包脚本要同步更新
这一块特别容易“本地能打包,CI 全挂”。
你贴的改名清单建议直接做两件事:
- 搜索你仓库里的旧 env 名称(GitHub Actions / GitLab CI / Jenkinsfile / bat/sh 脚本)
- 全量替换为新名称(例如
TAURI_PRIVATE_KEY→TAURI_SIGNING_PRIVATE_KEY等)
如果你有签名/自动发布流水线,这一步是必做项。
12. 迁移路线建议:按“最小可用”分阶段推进
为了避免一次性改太多导致不可控,我建议你按这个顺序做(每一步都能形成可运行状态):
阶段 A:基础可编译 + 前端可打开
- CLI/依赖升级
- 配置文件结构迁移(字段搬家、mainBinaryName、devUrl/frontendDist)
- JS 的 core 模块改名(tauri → core)
- Rust 入口整理(必要时把 run() 抽到 lib.rs,为移动端留口)
阶段 B:插件逐个恢复功能(每次迁移一个插件就验收一次)
- dialog / fs / shell / http / notification / os / process / clipboard / cli / global-shortcut / updater
阶段 C:权限系统补齐
- capability 文件审核与加固
- sidecar、remote domain、多窗口 target 范围验证
阶段 D:行为变化专项验证
- 事件系统是否广播导致串扰
- Windows origin 变化导致存储丢失(useHttpsScheme)
- tray/menu 行为是否符合预期
13. 最后给你一个“迁移验收 Checklist”(建议复制到你的 PR 描述里)
- 能
cargo tauri dev正常启动 - 生产构建能打包(Windows/macOS/Linux 各自至少跑一遍)
mainBinaryName与productName配置正确@tauri-apps/api/tauri全部替换为@tauri-apps/api/core- 用到的 v1 模块全部替换为对应插件,并完成 Rust
.plugin(...)初始化 src-tauri/capabilities/存在且覆盖你真实调用(特别是 fs/shell/sidecar/updater)- Updater 已实现手动检查与安装,否则用户后续无法更新
- Windows 下如需保留 https origin,已配置
useHttpsScheme - 事件系统升级后,窗口间事件不串台(或已改用 emit_to)