---
title: 从 Tauri 1.0 升级
i18nReady: true
sidebar:
  order: 15
---

import { Tabs, TabItem } from '@astrojs/starlight/components';
import CommandTabs from '@components/CommandTabs.astro';

本指南将会引导你将 Tauri 1.0 应用程序升级到 Tauri 2.0 版本。

## 针对移动端开发

Tauri 的移动端界面要求你的项目输出链接库（shared library）。如果你的现有应用程序以支持移动端为目标，则必须更改你的 crate 类型，以便在生成桌面端可执行文件的同时也生成链接库。

1. 添加下列配置到你的 Cargo 配置文件以生成链接库。

```toml
// src-tauri/Cargo.toml
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
```

2. 将 `src-tauri/src/main.rs` 重命名为 `src-tauri/src/lib.rs`。这一文件同时被桌面端和移动端的构建结果共享。

3. 将 `lib.rs` 中的 `main` 函数头修改为：

```rust
// src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    // 在这里编写你的代码
}
```

`tauri::mobile_entry_point` 宏会在编译为移动端库时处理你的入口函数。

4. 重新创建 `main.rs` 并调用刚刚创建的共享的 `run` 函数：

```rust
// src-tauri/src/main.rs
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

fn main() {
  app_lib::run();
}
```

## 自动迁移

Tauri v2 的命令行工具包括一个 `migrate` 命令，可自动执行大部分流程，并帮助你完成迁移：

<CommandTabs
  npm="npm install @tauri-apps/cli@latest
    npm run tauri migrate"
  yarn="yarn upgrade @tauri-apps/cli@latest
    yarn tauri migrate"
  pnpm="pnpm update @tauri-apps/cli@latest
    pnpm tauri migrate"
  cargo='cargo install tauri-cli --version "^2.0.0" --locked
    cargo tauri migrate'
/>

{/* TODO: 2.0 */}

在[命令行界面参考](/zh-cn/reference/cli/#migrate)中了解有关 `migrate` 命令的更多信息。

## 变化摘要

以下是 Tauri 1.0 到 Tauri 2.0 的变化摘要：

### Tauri 配置

- 移动 `package > productName` 和 `package > version` 至顶层字段。
- 生成的二进制文件不会根据 `productName` 自动重新命名，所以你需要在配置的顶层增加 `mainBinaryName` 来匹配 `productName`。
- 移除 `package`。
- 重命名 `tauri` 键为 `app`。
- 移除 `tauri > allowlist`。详情参考[迁移授权许可](#迁移授权许可)。
- 移动 `tauri > allowlist > protocol > assetScope` 至 `app > security > assetProtocol > scope`。
- 移动 `tauri > cli` 至 `plugins > cli`。
- 重命名 `tauri > windows > fileDropEnabled` 为 `app > windows > dragDropEnabled`。
- 移除 `tauri > updater > active`。
- 移除 `tauri > updater > dialog`。
- 移动 `tauri > updater` 至 `plugins > updater`。
- 增加 `bundle > createUpdaterArtifacts`。如果此应用程序使用了更新程序（app updater），则必须设定此值。
  - 当从使用 v1 版本已分发的程序升级时，需设定此值为 `v1Compatible`。详情请参考[升级向导](/zh-cn/plugin/updater/)。
- 重命名 `tauri > systemTray` 为 `app > trayIcon`。
- 移动 `tauri > pattern` 至 `app > security > pattern`。
- 移动 `tauri > bundle` 到顶层。
- 移动 `tauri > bundle > dmg` 至 `bundle > macOS > dmg`。
- 移动 `tauri > bundle > deb` 至 `bundle > linux > deb`。
- 移动 `tauri > bundle > appimage` 至 `bundle > linux > appimage`。
- 移除 `tauri > bundle > macOS > license`，使用 `bundle > licenseFile` 作为替代。
- 移除 `tauri > bundle > windows > wix > license`，使用 `bundle > licenseFile` 作为替代。
- 移除 `tauri > bundle > windows > nsis > license`， 使用 `bundle > licenseFile` 作为替代。
- 移动 `build > withGlobalTauri` 至 `app > withGlobalTauri`。
- 重命名 `build > distDir` 为 `frontendDist`。
- 重命名 `build > devPath` 为 `devUrl`。

[Tauri 2.0 配置 API 参考资料](/zh-cn/reference/config/)

### 全新的 Cargo 功能

- linux-protocol-body：启用自定义协议请求体解析，并允许 IPC 使用它。需要 webkit2gtk 2.40。

### 移除/修改的 Cargo 功能

- reqwest-client：reqwest 现在是唯一受支持的客户端。
- reqwest-native-tls-vendored：使用 `native-tls-vendored` 作为替代。
- process-command-api：使用 `shell` 插件作为替代（见下节说明）。
- shell-open-api：使用 `shell` 插件作为替代（见下节说明）。
- windows7-compat：被移动到 `notification` 插件。
- updater：updater 现在是一个插件。
- linux-protocol-headers：现在默认启动，因为我们更新了最低 webkit2gtk 版本支持。
- system-tray：重命名为 `tray-icon`。

### Rust Crate 变化

- `api` 模块被移除。每个 API 模块都可以在 Tauri 插件中找到。
- `api::dialog` 模块被移除。使用 `tauri-plugin-dialog` 作为替代。[查看详情](#对话窗插件迁移)
- `api::file` 模块被移除。使用 Rust 的 [`std::fs`](https://doc.rust-lang.org/std/fs/) 作为替代。
- `api::http` 模块被移除。使用 `tauri-plugin-http` 作为替代。[查看详情](#http-插件迁移)
- `api::ip` 模块被重写并被移动到 `tauri::ipc`。查看新的应用程序接口，特别是 `tauri::ipc::Channel`。
- `api::path` 模块中的函数和 `tauri::PathResolved` 被移动到 `tauri::Manager::path`。[查看详情](#迁移-path-到-tauri-manager)
- `api::process::Command`、`tauri::api::shell` 和 `tauri::Manager::shell_scope` API 被移除。使用 `tauri-plugin-shell` 作为替代。[查看详情](#shell-插件迁移)
- `api::process::current_binary` 和 `tauri::api::process::restart` 被移动到 `tauri::process`。
- `api::version` module has been 被移除。使用 [semver crate](https://docs.rs/semver/latest/semver/) 作为替代。
- `App::clipboard_manager` 和 `AppHandle::clipboard_manager` 被移除。使用 `tauri-plugin-clipboard` 作为替代。[查看详情](#剪贴板插件迁移)
- `App::get_cli_matches` 被移除。使用 `tauri-plugin-cli` 作为替代。[查看详情](#命令行工具插件迁移)
- `App::global_shortcut_manager` 和 `AppHandle::global_shortcut_manager` 被移除。使用 `tauri-plugin-global-shortcut` 作为替代。[查看详情](#全局快捷方式插件迁移)
- `Manager::fs_scope` 被移除。文件系统范围可通过 `tauri_plugin_fs::FsExt` 访问。
- `Plugin::PluginApi` 现在会接收一个插件的配置作为第二个参数。
- `Plugin::setup_with_config` 被移除。使用更新后的 `tauri::Plugin::PluginApi` 作为替代。
- `scope::ipc::RemoteDomainAccessScope::enable_tauri_api` 和 `scope::ipc::RemoteDomainAccessScope::enables_tauri_api` 被移除。通过 `scope::ipc::RemoteDomainAccessScope::add_plugin` 单独启用每个核心插件。
- `scope::IpcScope` 被移除。使用 `scope::ipc::Scope` 作为替代。
- `scope::FsScope`、`scope::GlobPattern` 和 `scope::FsScopeEvent` 被移除。分别使用 `scope::fs::Scope`、`scope::fs::Pattern` 和 `scope::fs::Event` 作为替代。
- `updater` 模块被移除。使用 `tauri-plugin-updater` 作为替代。[查看详情](#更新插件迁移)
- `Env.args` 字段被移除。使用 `Env.args_os` 字段作为替代。
- `Menu`、`MenuEvent`、`CustomMenuItem`、`Submenu`、`WindowMenuEvent`、`MenuItem` 和 `Builder::on_menu_event` API 被移除。[查看详情](#托盘菜单迁移)
- `SystemTray`、`SystemTrayHandle`、`SystemTrayMenu`、`SystemTrayMenuItemHandle`、`SystemTraySubmenu`、`MenuEntry` 和 `SystemTrayMenuItem` API 被移除。[查看详情](#托盘图标模块迁移)

### JavaScript API 变化

`@tauri-apps/api` 包不再提供非核心模块。只有先前的 `tauri`（现在的 `core`）、`path`、`event` 和 `window` 模块被导出。
其他的模块被分离移至插件中。

- `@tauri-apps/api/tauri` 模块被重命名为 `@tauri-apps/api/core`。[查看详情](#core-模块迁移)
- `@tauri-apps/api/cli` 模块被移除。使用 `@tauri-apps/plugin-cli` 作为替代。[查看详情](#命令行工具插件迁移)
- `@tauri-apps/api/clipboard` 模块被移除。使用 `@tauri-apps/plugin-clipboard` 作为替代。[查看详情](#剪贴板插件迁移)
- `@tauri-apps/api/dialog` 模块被移除。使用 `@tauri-apps/plugin-dialog` 作为替代。[查看详情](#对话窗插件迁移)
- `@tauri-apps/api/fs` 模块被移除。使用 `@tauri-apps/plugin-fs` 作为替代。[查看详情](#文件系统插件迁移)
- `@tauri-apps/api/global-shortcut` 模块被移除。使用 `@tauri-apps/plugin-global-shortcut` 作为替代。[查看详情](#全局快捷方式插件迁移)
- `@tauri-apps/api/http` 模块被移除。使用 `@tauri-apps/plugin-http` 作为替代。[查看详情](#http-插件迁移)
- `@tauri-apps/api/os` 模块被移除。使用 `@tauri-apps/plugin-os` 作为替代。[查看详情](#操作系统插件迁移)
- `@tauri-apps/api/notification` 模块被移除。使用 `@tauri-apps/plugin-notification` 作为替代。[查看详情](#提示信息插件迁移)
- `@tauri-apps/api/process` 模块被移除。使用 `@tauri-apps/plugin-process` 作为替代。[查看详情](#进程插件迁移)
- `@tauri-apps/api/shell` 模块被移除。使用 `@tauri-apps/plugin-shell` 作为替代。[查看详情](#shell-插件迁移)
- `@tauri-apps/api/updater` 模块被移除。使用 `@tauri-apps/plugin-updater` 作为替代。[查看详情](#更新插件迁移)
- `@tauri-apps/api/window` 模块被重命名为 `@tauri-apps/api/webviewWindow`。[查看详情](#新的窗口-api-迁移)

### 环境变量变化

Tauri 命令行工具读取和写入的大部分环境变量都重新命名，以保持一致性并防止出错：

- `TAURI_PRIVATE_KEY` -> `TAURI_SIGNING_PRIVATE_KEY`
- `TAURI_KEY_PASSWORD` -> `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`
- `TAURI_SKIP_DEVSERVER_CHECK` -> `TAURI_CLI_NO_DEV_SERVER_WAIT`
- `TAURI_DEV_SERVER_PORT` -> `TAURI_CLI_PORT`
- `TAURI_PATH_DEPTH` -> `TAURI_CLI_CONFIG_DEPTH`
- `TAURI_FIPS_COMPLIANT` -> `TAURI_BUNDLER_WIX_FIPS_COMPLIANT`
- `TAURI_DEV_WATCHER_IGNORE_FILE` -> `TAURI_CLI_WATCHER_IGNORE_FILENAME`
- `TAURI_TRAY` -> `TAURI_LINUX_AYATANA_APPINDICATOR`
- `TAURI_APPLE_DEVELOPMENT_TEAM` -> `APPLE_DEVELOPMENT_TEAM`
- `TAURI_PLATFORM` -> `TAURI_ENV_PLATFORM`
- `TAURI_ARCH` -> `TAURI_ENV_ARCH`
- `TAURI_FAMILY` -> `TAURI_ENV_FAMILY`
- `TAURI_PLATFORM_VERSION` -> `TAURI_ENV_PLATFORM_VERSION`
- `TAURI_PLATFORM_TYPE` -> `TAURI_ENV_PLATFORM_TYPE`
- `TAURI_DEBUG` -> `TAURI_ENV_DEBUG`

### 事件系统

事件系统经过重新设计，更易于使用。现在，它不再依赖于事件源，而是依赖于事件目标，实现起来更加简单。

- `emit` 函数现在会向所有事件侦听器发送事件。
- 添加一个新的 `emit_to` 函数以向特定目标触发事件。
- `emit_filter` 现在基于 [`EventTarget`](https://docs.rs/tauri/2.0.0/tauri/event/enum.EventTarget.html) 进行过滤，而不是基于一个视窗。
- 将 `listen_global` 重命名为 `listen_any`。现在，它可以监听所有事件，无论其过滤器和目标是什么。

### 多 webview 支持

Tauri v2 引入了多 webview 支持，目前处在 `unstable` feature 标识之后。
为了支持这一功能，我们将 Rust 的 `Window` 类型重命名为 `WebviewWindow`、`Manager` 的 `get_window` 函数重命名为 `get_webview_window`。

`WebviewWindow` JS API 类型现在是从 `@tauri-apps/api/webviewWindow` 中重导出，而不是从 `@tauri-apps/api/window` 中。

## 具体迁移细节

将 Tauri 1.0 应用程序迁移到 Tauri 2.0 时可能遇到的常见情况。

### `core` 模块迁移

`@tauri-apps/api/tauri` 模块被重命名为 `@tauri-apps/api/core`。
只需要修改你的导入路径：

```diff
- import { invoke } from "@tauri-apps/api/tauri"
+ import { invoke } from "@tauri-apps/api/core"
```

### 命令行工具插件迁移

Rust 的 `App::get_cli_matches` 和 JavaScript 的 `@tauri-apps/api/cli` API 被移除。使用 `@tauri-apps/plugin-cli` 插件作为替代：

1. 添加 Cargo 依赖项：

```toml
# Cargo.toml
[dependencies]
tauri-plugin-cli = "2"
```

2. 在 JavaScript 或 Rust 项目中使用：

<Tabs syncKey="lang">
<TabItem label="JavaScript">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_cli::init())
}
```

```json
// package.json
{
  "dependencies": {
    "@tauri-apps/plugin-cli": "^2.0.0"
  }
}
```

```javascript
import { getMatches } from '@tauri-apps/plugin-cli';
const matches = await getMatches();
```

</TabItem>
<TabItem label="Rust">

```rust
fn main() {
    use tauri_plugin_cli::CliExt;
    tauri::Builder::default()
        .plugin(tauri_plugin_cli::init())
        .setup(|app| {
            let cli_matches = app.cli().matches()?;
            Ok(())
        })
}
```

</TabItem>
</Tabs>

### 剪贴板插件迁移

Rust 的 `App::clipboard_manager` 和 `AppHandle::clipboard_manager` 以及 JavaScript 的 `@tauri-apps/api/clipboard` API 被移除。使用 `@tauri-apps/plugin-clipboard-manager` 插件作为替代：

```toml
[dependencies]
tauri-plugin-clipboard-manager = "2"
```

<Tabs syncKey="lang">
<TabItem label="JavaScript">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_clipboard_manager::init())
}
```

```json
// package.json
{
  "dependencies": {
    "@tauri-apps/plugin-clipboard-manager": "^2.0.0"
  }
}
```

```javascript
import { writeText, readText } from '@tauri-apps/plugin-clipboard-manager';
await writeText('Tauri is awesome!');
assert(await readText(), 'Tauri is awesome!');
```

</TabItem>
<TabItem label="Rust">

```rust
use tauri_plugin_clipboard::{ClipboardExt, ClipKind};
tauri::Builder::default()
    .plugin(tauri_plugin_clipboard::init())
    .setup(|app| {
        app.clipboard().write(ClipKind::PlainText {
            label: None,
            text: "Tauri is awesome!".into(),
        })?;
        Ok(())
    })
```

</TabItem>
</Tabs>

### 对话窗插件迁移

Rust 的 `tauri::api::dialog` 和 JavaScript 的 `@tauri-apps/api/dialog` API 被移除。使用 `@tauri-apps/plugin-dialog` 插件作为替代：

1. 添加 Cargo 依赖项：

```toml
# Cargo.toml
[dependencies]
tauri-plugin-dialog = "2"
```

2. 在 JavaScript 或 Rust 项目中使用：

<Tabs syncKey="lang">
<TabItem label="JavaScript">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_dialog::init())
}
```

```json
// package.json
{
  "dependencies": {
    "@tauri-apps/plugin-dialog": "^2.0.0"
  }
}
```

```javascript
import { save } from '@tauri-apps/plugin-dialog';
const filePath = await save({
  filters: [
    {
      name: 'Image',
      extensions: ['png', 'jpeg'],
    },
  ],
});
```

</TabItem>
<TabItem label="Rust">

```rust
use tauri_plugin_dialog::DialogExt;
tauri::Builder::default()
    .plugin(tauri_plugin_dialog::init())
    .setup(|app| {
        app.dialog().file().pick_file(|file_path| {
            // 对此处的可选文件路径进行处理
            // 如果用户关闭了对话框，则文件路径为 `None`。
        });

        app.dialog().message("Tauri is Awesome!").show();
        Ok(())
    })
```

</TabItem>
</Tabs>

### 文件系统插件迁移

Rust 的 `App::get_cli_matches` 和 JavaScript 的 `@tauri-apps/api/fs` API 被移除。使用 Rust 的 [`std::fs`](https://doc.rust-lang.org/std/fs/) 或 JavaScript 的 `@tauri-apps/plugin-fs` 插件作为替代：

1. 添加 Cargo 依赖项：

```toml
# Cargo.toml
[dependencies]
tauri-plugin-fs = "2"
```

2. 在 JavaScript 或 Rust 项目中使用：

<Tabs syncKey="lang">
<TabItem label="JavaScript">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_fs::init())
}
```

```json
// package.json
{
  "dependencies": {
    "@tauri-apps/plugin-fs": "^2.0.0"
  }
}
```

```javascript
import { mkdir, BaseDirectory } from '@tauri-apps/plugin-fs';
await mkdir('db', { baseDir: BaseDirectory.AppLocalData });
```

一些函数和类型被重命名或移除：

- `Dir` 枚举别名（enum alias）被移除。使用 `BaseDirectory`。
- `FileEntry`、`FsBinaryFileOption`、`FsDirOptions`、`FsOptions`、`FsTextFileOption` 和 `BinaryFileContents` 接口和类型别名被移除，并被与每个对应的函数相匹配的接口取代。
- `createDir` 被重命名为 `mkdir`。
- `readBinaryFile` 被重命名为 `readFile`。
- `removeDir` 被移除并被 `remove` 取代。
- `removeFile` 被移除并被 `remove` 取代。
- `renameFile` 被移除并被 `rename` 取代。
- `writeBinaryFile` 被重命名为 `writeFile`。

</TabItem>
<TabItem label="Rust">

使用 Rust [`std::fs`](https://doc.rust-lang.org/std/fs/) 中的函数。

</TabItem>
</Tabs>

### 全局快捷方式插件迁移

Rust 的 `App::global_shortcut_manager` 和 `AppHandle::global_shortcut_manager` 以及 JavaScript 的 `@tauri-apps/api/global-shortcut` API 被移除。使用 `@tauri-apps/plugin-global-shortcut` 插件作为替代：

1. 添加 Cargo 依赖项：

```toml
# Cargo.toml
[dependencies]
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
tauri-plugin-global-shortcut = "2"
```

2. 在 JavaScript 或 Rust 项目中使用：

<Tabs syncKey="lang">
<TabItem label="JavaScript">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_global_shortcut::Builder::default().build())
}
```

```json
// package.json
{
  "dependencies": {
    "@tauri-apps/plugin-global-shortcut": "^2.0.0"
  }
}
```

```javascript
import { register } from '@tauri-apps/plugin-global-shortcut';
await register('CommandOrControl+Shift+C', () => {
  console.log('Shortcut triggered');
});
```

</TabItem>
<TabItem label="Rust">

```rust
use tauri_plugin_global_shortcut::GlobalShortcutExt;

tauri::Builder::default()
    .plugin(
        tauri_plugin_global_shortcut::Builder::new().with_handler(|app, shortcut| {
            println!("Shortcut triggered: {:?}", shortcut);
        })
        .build(),
    )
    .setup(|app| {
        // 注册全局快捷方式
        // 在 macOS 上使用 Cmd 键
        // 在 Windows 和 Linux 系统中，使用 Ctrl 键
        app.global_shortcut().register("CmdOrCtrl+Y")?;
        Ok(())
    })
```

</TabItem>
</Tabs>

### HTTP 插件迁移

Rust 的 `tauri::api::http` 和 JavaScript 的 `@tauri-apps/api/http` API 被移除。使用 `@tauri-apps/plugin-http` 插件作为替代：

1. 添加 Cargo 依赖项：

```toml
# Cargo.toml
[dependencies]
tauri-plugin-http = "2"
```

2. 在 JavaScript 或 Rust 项目中使用：

<Tabs syncKey="lang">
<TabItem label="JavaScript">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_http::init())
}
```

```json
// package.json
{
  "dependencies": {
    "@tauri-apps/plugin-http": "^2.0.0"
  }
}
```

```javascript
import { fetch } from '@tauri-apps/plugin-http';
const response = await fetch(
  'https://raw.githubusercontent.com/tauri-apps/tauri/dev/package.json'
);
```

</TabItem>
<TabItem label="Rust">

```rust
use tauri_plugin_http::reqwest;

tauri::Builder::default()
    .plugin(tauri_plugin_http::init())
    .setup(|app| {
        let response_data = tauri::async_runtime::block_on(async {
            let response = reqwest::get(
                "https://raw.githubusercontent.com/tauri-apps/tauri/dev/package.json",
            )
            .await
            .unwrap();
            response.text().await
        })?;
        Ok(())
    })
```

HTTP 插件重导出了 [reqwest](https://docs.rs/reqwest/latest/reqwest/)。所以你可以查阅它的文档以获取更多信息。

</TabItem>
</Tabs>

### 提示信息插件迁移

Rust 的 `tauri::api::notification` 和 JavaScript 的 `@tauri-apps/api/notification` API 被移除。使用 `@tauri-apps/plugin-notification` 插件作为替代：

1. 添加 Cargo 依赖项：

```toml
# Cargo.toml
[dependencies]
tauri-plugin-notification = "2"
```

2. 在 JavaScript 或 Rust 项目中使用：

<Tabs syncKey="lang">
<TabItem label="JavaScript">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_notification::init())
}
```

```json
// package.json
{
  "dependencies": {
    "@tauri-apps/plugin-notification": "^2.0.0"
  }
}
```

```javascript
import { sendNotification } from '@tauri-apps/plugin-notification';
sendNotification('Tauri is awesome!');
```

</TabItem>
<TabItem label="Rust">

```rust
use tauri_plugin_notification::NotificationExt;
use tauri::plugin::PermissionState;

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_notification::init())
        .setup(|app| {
            if app.notification().permission_state()? == PermissionState::Unknown {
                app.notification().request_permission()?;
            }
            if app.notification().permission_state()? == PermissionState::Granted {
                app.notification()
                    .builder()
                    .body("Tauri is awesome!")
                    .show()?;
            }
            Ok(())
        })
}
```

</TabItem>
</Tabs>

### 菜单模块迁移

Rust 的 `Menu` API 被移动到 `tauri::menu` 模块，并使用 [muda crate](https://github.com/tauri-apps/muda) 重构。

#### 使用 `tauri::menu::MenuBuilder`

使用 `tauri::menu::MenuBuilder` 以替换 `tauri::Menu`。
注意到它的构造器接收一个 `Manager` 实例（`App`、`AppHandle` 或 `WebviewWindow` 中的一个）作为入参：

```rust
use tauri::menu::MenuBuilder;

tauri::Builder::default()
    .setup(|app| {
        let menu = MenuBuilder::new(app)
            .copy()
            .paste()
            .separator()
            .undo()
            .redo()
            .text("open-url", "Open URL")
            .check("toggle", "Toggle")
            .icon("show-app", "Show App", app.default_window_icon().cloned().unwrap())
            .build()?;
        Ok(())
    })
```

#### 使用 `tauri::menu::PredefinedMenuItem`

使用 `tauri::menu::PredefinedMenuItem` 以替换 `tauri::MenuItem`：

```rust
use tauri::menu::{MenuBuilder, PredefinedMenuItem};

tauri::Builder::default()
    .setup(|app| {
        let menu = MenuBuilder::new(app).item(&PredefinedMenuItem::copy(app)?).build()?;
        Ok(())
    })
```

:::tip

`Menu` 构造器有专门的方法来添加每个预定义的菜单项，因此您可以调用 `.copy()` 以替换 `.item(&PredefinedMenuItem::copy(app,None)?)`。

:::

#### 使用 `tauri::menu::MenuItemBuilder`

使用 `tauri::menu::MenuItemBuilder` 以替换 `tauri::CustomMenuItem`：

```rust
use tauri::menu::MenuItemBuilder;

tauri::Builder::default()
    .setup(|app| {
        let toggle = MenuItemBuilder::new("Toggle").accelerator("Ctrl+Shift+T").build(app)?;
        Ok(())
    })
```

#### 使用 `tauri::menu::SubmenuBuilder`

使用 `tauri::menu::SubmenuBuilder` 以替换 `tauri::Submenu`：

```rust
use tauri::menu::{MenuBuilder, SubmenuBuilder};

tauri::Builder::default()
    .setup(|app| {
        let submenu = SubmenuBuilder::new(app, "Sub")
            .text("Tauri")
            .separator()
            .check("Is Awesome")
            .build()?;
        let menu = MenuBuilder::new(app).item(&submenu).build()?;
        Ok(())
    })
```

`tauri::Builder::menu` 现在接收一个闭包，因为菜单需要一个 `Manager` 实例来构造。参考[相关文档](https://docs.rs/tauri/2.0.0/tauri/struct.Builder.html#method.menu)以获取更多信息。

#### 菜单触发事件迁移

Rust 的 `tauri::Builder::on_menu_event` API 被移除。使用 `tauri::App::on_menu_event` 或 `tauri::AppHandle::on_menu_event` 作为替代：

```rust
use tauri::menu::{CheckMenuItemBuilder, MenuBuilder, MenuItemBuilder};

tauri::Builder::default()
    .setup(|app| {
        let toggle = MenuItemBuilder::with_id("toggle", "Toggle").build(app)?;
        let check = CheckMenuItemBuilder::new("Mark").build(app)?;
        let menu = MenuBuilder::new(app).items(&[&toggle, &check]).build()?;

        app.set_menu(menu)?;

        app.on_menu_event(move |app, event| {
            if event.id() == check.id() {
                println!("`check` triggered, do something! is checked? {}", check.is_checked().unwrap());
            } else if event.id() == "toggle" {
                println!("toggle triggered!");
            }
        });
        Ok(())
    })
```

请注意，有两种方法可以检查哪个菜单项被选中：将项目移至事件处理程序闭包并比较 ID，或通过 `with_id` 构造函数为项目定义自定义 ID，并使用该 ID 字符串进行比较。

:::tip

菜单项可以跨菜单共享，菜单事件绑定到菜单项而不是菜单或窗口。

如果不想在选中菜单项时触发所有监听器，请不要共享菜单项，而使用可以 move 到 `tauri::WebviewWindow/WebviewWindowBuilder::on_menu_event` 的闭包中的专有实例。

:::

### 操作系统插件迁移

Rust 的 `tauri::api::os` 和 JavaScript 的 `@tauri-apps/api/os` API 被移除。使用 `@tauri-apps/plugin-os` 插件作为替代：

1. 添加 Cargo 依赖项：

```toml
# Cargo.toml
[dependencies]
tauri-plugin-os = "2"
```

2. 在 JavaScript 或 Rust 项目中使用：

<Tabs syncKey="lang">
<TabItem label="JavaScript">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_os::init())
}
```

```json
// package.json
{
  "dependencies": {
    "@tauri-apps/plugin-os": "^2.0.0"
  }
}
```

```javascript
import { arch } from '@tauri-apps/plugin-os';
const architecture = await arch();
```

</TabItem>
<TabItem label="Rust">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_os::init())
        .setup(|app| {
            let os_arch = tauri_plugin_os::arch();
            Ok(())
        })
}
```

</TabItem>
</Tabs>

### 进程插件迁移

Rust 的 `tauri::api::process` 和 JavaScript 的 `@tauri-apps/api/process` API 被移除。使用 `@tauri-apps/plugin-process` 插件作为替代：

1. 添加到 Cargo 依赖项：

```toml
# Cargo.toml
[dependencies]
tauri-plugin-process = "2"
```

2. 在 JavaScript 或 Rust 项目中使用：

<Tabs syncKey="lang">
<TabItem label="JavaScript">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_process::init())
}
```

```json
// package.json
{
  "dependencies": {
    "@tauri-apps/plugin-process": "^2.0.0"
  }
}
```

```javascript
import { exit, relaunch } from '@tauri-apps/plugin-process';
await exit(0);
await relaunch();
```

</TabItem>
<TabItem label="Rust">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_process::init())
        .setup(|app| {
            // 以给定的状态码退出应用程序
            app.handle().exit(1);
            // 重新启动应用程序
            app.handle().restart();
            Ok(())
        })
}
```

</TabItem>
</Tabs>

### Shell 插件迁移

Rust 的 `tauri::api::shell` 和 JavaScript 的 `@tauri-apps/api/shell` API 被移除。使用 `@tauri-apps/plugin-shell` 插件作为替代：

1. 添加 Cargo 依赖项：

```toml
# Cargo.toml
[dependencies]
tauri-plugin-shell = "2"
```

2. 在 JavaScript 或 Rust 项目中使用：

<Tabs syncKey="lang">
<TabItem label="JavaScript">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
}
```

```json
// package.json
{
  "dependencies": {
    "@tauri-apps/plugin-shell": "^2.0.0"
  }
}
```

```javascript
import { Command, open } from '@tauri-apps/plugin-shell';
const output = await Command.create('echo', 'message').execute();

await open('https://github.com/tauri-apps/tauri');
```

</TabItem>
<TabItem label="Rust">

- 打开一个 URL：

```rust
use tauri_plugin_shell::ShellExt;

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .setup(|app| {
            app.shell().open("https://github.com/tauri-apps/tauri", None)?;
            Ok(())
        })
}
```

- 生成子进程并获取状态码：

```rust
use tauri_plugin_shell::ShellExt;

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .setup(|app| {
            let status = tauri::async_runtime::block_on(async move { app.shell().command("which").args(["ls"]).status().await.unwrap() });
            println!("`which` finished with status: {:?}", status.code());
            Ok(())
        })
}
```

- 生成子进程并捕获其输出：

```rust
use tauri_plugin_shell::ShellExt;

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .setup(|app| {
            let output = tauri::async_runtime::block_on(async move { app.shell().command("echo").args(["TAURI"]).output().await.unwrap() });
            assert!(output.status.success());
            assert_eq!(String::from_utf8(output.stdout).unwrap(), "TAURI");
            Ok(())
        })
}
```

- 生成一个子进程并异步读取其事件：

```rust
use tauri_plugin_shell::{ShellExt, process::CommandEvent};

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .setup(|app| {
            let handle = app.handle().clone();
            tauri::async_runtime::spawn(async move {
                let (mut rx, mut child) = handle.shell().command("cargo")
                    .args(["tauri", "dev"])
                    .spawn()
                    .expect("Failed to spawn cargo");

                let mut i = 0;
                while let Some(event) = rx.recv().await {
                    if let CommandEvent::Stdout(line) = event {
                        println!("got: {}", String::from_utf8(line).unwrap());
                        i += 1;
                        if i == 4 {
                            child.write("message from  Rust\n".as_bytes()).unwrap();
                            i = 0;
                        }
                    }
                }
            });
            Ok(())
        })
}
```

</TabItem>
</Tabs>

### 托盘图标模块迁移

Rust 的 `SystemTray` API 被重命名为 `TrayIcon` 以确保一致性。新的 API 可以在 Rust 的 `tray` 模块中找到。

#### 使用 `tauri::tray::TrayIconBuilder`

使用 `tauri::tray::TrayIconBuilder` 以替换 `tauri::SystemTray`：

```rust
let tray = tauri::tray::TrayIconBuilder::with_id("my-tray").build(app)?;
```

查阅 [`TrayIconBuilder`](https://docs.rs/tauri/2.0.0/tauri/tray/struct.TrayIconBuilder.html) 以获取更多信息。

#### 托盘菜单迁移

使用 `tauri::menu::Menu` 以替换 `tauri::SystemTrayMenu`、`tauri::menu::Submenu` 以替换 `tauri::SystemTraySubmenu` 和 `tauri::menu::PredefinedMenuItem` 以替换 `tauri::SystemTrayMenuItem`。

#### 托盘事件迁移

`tauri::SystemTray::on_event` 被分割为 `tauri::tray::TrayIconBuilder::on_menu_event` 和 `tauri::tray::TrayIconBuilder::on_tray_icon_event`：

```rust
use tauri::{
    menu::{MenuBuilder, MenuItemBuilder},
    tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
};

tauri::Builder::default()
    .setup(|app| {
        let toggle = MenuItemBuilder::with_id("toggle", "Toggle").build(app)?;
        let menu = MenuBuilder::new(app).items(&[&toggle]).build()?;
        let tray = TrayIconBuilder::new()
            .menu(&menu)
            .on_menu_event(move |app, event| match event.id().as_ref() {
                "toggle" => {
                    println!("toggle clicked");
                }
                _ => (),
            })
            .on_tray_icon_event(|tray, event| {
                if let TrayIconEvent::Click {
                        button: MouseButton::Left,
                        button_state: MouseButtonState::Up,
                        ..
                } = event
                {
                    let app = tray.app_handle();
                    if let Some(webview_window) = app.get_webview_window("main") {
                       let _ = webview_window.unminimize();
                       let _ = webview_window.show();
                       let _ = webview_window.set_focus();
                    }
                }
            })
            .build(app)?;

        Ok(())
    })
```

### 更新插件迁移

内置的自动更新的提示窗被移除。作为代替，使用 Rust 和 JS API 以检查和安装更新。

Rust 的 `tauri::updater` 和 JavaScript 的 `@tauri-apps/api-updater` API 被移除。
使用 `@tauri-apps/plugin-updater` 设置一个自定义的更新器：

1. 添加 Cargo 依赖项：

```toml
[dependencies]
tauri-plugin-updater = "2"
```

2. 在 JavaScript 或 Rust 项目中使用：

<Tabs syncKey="lang">
<TabItem label="JavaScript">

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_updater::Builder::new().build())
}
```

```json
// package.json
{
  "dependencies": {
    "@tauri-apps/plugin-updater": "^2.0.0"
  }
}
```

```javascript
import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';

const update = await check();
if (update?.available) {
  console.log(`Update to ${update.version} available! Date: ${update.date}`);
  console.log(`Release notes: ${update.body}`);
  await update.downloadAndInstall();
  // 需要 `process` 插件
  await relaunch();
}
```

</TabItem>
<TabItem label="Rust">

检查更新：

```rust
use tauri_plugin_updater::UpdaterExt;

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_updater::Builder::new().build())
        .setup(|app| {
            let handle = app.handle();
            tauri::async_runtime::spawn(async move {
                let response = handle.updater().check().await;
            });
            Ok(())
        })
}
```

设置自定义更新目标：

```rust
fn main() {
    let mut updater = tauri_plugin_updater::Builder::new();
    #[cfg(target_os = "macos")]
    {
        updater = updater.target("darwin-universal");
    }
    tauri::Builder::default()
        .plugin(updater.build())
}
```

</TabItem>
</Tabs>

### 迁移 `Path` 到 Tauri `Manager`

Rust 的 `tauri::api::path` 模块的函数和 `tauri::PathResolver` 被移动到 `tauri::Manager::path`：

```rust
use tauri::{path::BaseDirectory, Manager};

tauri::Builder::default()
    .setup(|app| {
        let home_dir_path = app.path().home_dir().expect("failed to get home dir");

        let path = app.path().resolve("path/to/something", BaseDirectory::Config)?;

        Ok(())
    })
```

### 新的窗口 API 迁移

在 Rust 侧，`Window` 被重命名为 `WebviewWindow`，它的构建器 `WindowBuilder` 被重命名为 `WebviewWindowBuilder`。

另外，`Manager::get_window` 函数被重命名为 `get_webview_window`，窗口的 `parent_window` API 被重命名为 `parent_raw` 以支持高级窗口父 API。

在 JavaScript 侧，`WebviewWindow` 类现在从 `@tauri-apps/api/webviewWindow` 中重导出。

`onMenuClicked` 函数已被移除。你可以在 JavaScript 中创建菜单时拦截菜单事件。

### 迁移嵌入的附加文件 (Resources)

在 JavaScript 侧，请确保[迁移文件系统插件](#文件系统插件迁移)。

此外，请注意[迁移授权许可](#迁移授权许可)中对 v1 的授权许可列表所作的更改。

在 Rust 侧，请确保[将 Path 迁移到 Tauri Manager](#迁移-path-到-tauri-manager).

### 迁移嵌入的外部二进制文档 (Sidecar)

在 Tauri v1 中，外部的二进制文件和其参数在许可列表中被定义。而在 v2 中，使用了新的权限系统。有关详细信息，请阅读[迁移授权许可](#迁移授权许可)。

在 JavaScript 侧，请确保[迁移 Shell 插件](#shell-插件迁移)。

在 Rust 侧，`tauri::api::process` API 已经被移除，使用 `tauri_plugin_shell::ShellExt` 和 `tauri_plugin_shell::process::CommandEvent` 作为替代。[嵌入的外部二进制文档](/zh-cn/develop/sidecar/#从-rust-运行)将指导你如何迁移。

在 v2 中移除了 "process-command-api" 特性。因此，运行外部的二进制文件不需要再在 Tauri 配置文件中配置。

### 迁移授权许可

授权许可列表 v1 已被重写为一个全新的权限系统，可用于单个插件，并可为多窗口和远程 URL 支持进行更多配置。
这个新系统的工作原理类似于访问控制列表（ACL），你可以允许或拒绝命令、将权限分配给一组特定的窗口和域，并定义访问范围。

要为应用程序启用权限，您必须在 `src-tauri/capabilities` 文件夹内创建许可配置文件，随后 Tauri 会自动为您配置其他一切。

`migrate` 命令行工具命令会自动解析你的授权许可列表 v1 并生成相关的许可配置文件。

要了解有关权限和功能的更多信息，请参阅[安全文档](/zh-cn/security/)。
