mcpField
T
tauri-v2

tauri-v2

github.com/nodnarbnitram开发168.1k 次安装27 周前更新
代码仓库

关于 tauri-v2

tauri-v2 是面向 Tauri v2+ 开发者的开发技能,提供桌面和移动应用构建指导。内容聚焦 Rust 与 Web 前端交互、权限配置及常见构建和运行问题。

主要功能

  1. 01Tauri v2 命令与 IPC 模式
  2. 02能力与权限配置指导
  3. 03Rust 状态管理和错误处理
  4. 04桌面与移动构建注意事项
  5. 05常见问题排查参考

适用场景

  1. 01搭建 Tauri v2 应用结构
  2. 02排查命令调用或权限错误
  3. 03准备移动端构建配置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nodnarbnitram/claude-code-extensions --skill tauri-v2

安装后在 Claude Code 中输入/tauri-v2确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
references/
advanced-runtime-reference.md
capabilities-reference.md
ipc-patterns.md
plugin-reference.md
README.md
updater-distribution-reference.md

详细介绍

Tauri v2+ 开发技能

使用 Web 前端和 Rust 后端构建跨平台桌面与移动应用。

开始之前

此技能可避免 8 个以上的常见错误,并节省约 60% 的 Token。

指标不使用技能使用技能
设置时间约 2 小时约 30 分钟
常见错误8 个以上0
Token 用量高(探索)低(直接使用模式)
此技能可避免的已知问题
  1. 因缺少 capabilities 而出现权限被拒绝错误
  2. 因未在 generate_handler! 中注册命令而导致 IPC 失败
  3. 因类型不匹配而导致状态管理 panic
  4. 因缺少 Rust 目标而导致移动端构建失败
  5. 因开发 URL 配置错误而出现白屏问题

快速开始

第 1 步:创建 Tauri 命令
// src-tauri/src/lib.rs
#[tauri::command]
fn greet(name: String) -> String {
    format!("Hello, {}!", name)
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

为何重要: 未加入 generate_handler![] 的命令在前端调用时会静默失败。

main.rs 保持精简: src-tauri/src/main.rs 应仅作为精简的转发入口——所有应用逻辑都放在 lib.rs 中:

// src-tauri/src/main.rs
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
    app_lib::run();
}

移动端构建必须采用这种拆分方式——在移动端目标上,Tauri 会用 mobile_entry_point 替换 main()。

第 2 步:从前端调用
import { invoke } from '@tauri-apps/api/core';

const greeting = await invoke<string>('greet', { name: 'World' });
console.log(greeting); // "Hello, World!"

为何重要: 使用 @tauri-apps/api/core(而非 @tauri-apps/api/tauri——那是 v1 API)。

第 3 步:添加所需权限
// src-tauri/capabilities/default.json
{
    "$schema": "../gen/schemas/desktop-schema.json",
    "identifier": "default",
    "windows": ["main"],
    "permissions": ["core:default"]
}

为何重要: Tauri v2 默认拒绝所有操作——所有操作都必须显式授予权限。

关键规则

务必做到
  • 在 tauri::generate_handler![cmd1, cmd2, ...] 中注册每个命令
  • 命令返回 Result<T, E>,以便妥善处理错误
  • 对多个命令共同访问的共享状态使用 Mutex<T>
  • 使用任何插件功能前先添加 capabilities
  • 使用 lib.rs 存放共享代码(移动端构建所必需)
  • 为兼容移动端,在 lib.rs 的 pub fn run() 上使用 #[cfg_attr(mobile, tauri::mobile_entry_point)]
切勿这样做
  • 切勿在异步命令中使用借用类型(&str)——改用拥有所有权的类型
  • 切勿阻塞主线程——对 I/O 操作使用异步
  • 切勿硬编码路径——使用 Tauri 路径 API(app.path())
  • 切勿跳过 capability 配置——即使是“安全”的操作也需要权限
常见错误

错误——在异步命令中使用借用类型:

#[tauri::command]
async fn bad(name: &str) -> String { // Compile error!
    name.to_string()
}

正确——使用拥有所有权的类型:

#[tauri::command]
async fn good(name: String) -> String {
    name
}

原因: 异步命令无法在 await 点之间借用数据;Tauri 要求异步命令参数使用拥有所有权的类型。

预防已知问题

问题根本原因解决方案
“找不到命令”未加入 generate_handler!将命令添加到处理程序宏中
“权限被拒绝”缺少 capability添加到 capabilities/default.json
插件功能静默失败已安装插件,但 capability 中未授予权限将插件权限字符串添加到 capabilities/default.json
更新器在生产环境中失败工件未签名或使用了 HTTP 端点使用 cargo tauri signer generate 生成密钥,并且仅使用 HTTPS 端点
找不到 Sidecartauri.conf.json 中没有 externalBin,或缺少可执行文件将路径添加到 bundle.externalBin,并确保二进制文件已打包
功能在桌面端正常,在移动端失效使用了仅限桌面端的 API检查 API 是否支持移动端——部分插件仅适用于桌面端
访问状态时 panicState<T> 中的类型不匹配使用 .manage() 中的确切类型
启动时白屏前端未构建检查配置中的 beforeDevCommand
IPC 超时异步命令阻塞移除阻塞代码或使用 spawn
移动端构建失败缺少 Rust 目标运行 rustup target add <target>

深入参考

配置参考

tauri.conf.json
{
    "$schema": "./gen/schemas/desktop-schema.json",
    "productName": "my-app",
    "version": "1.0.0",
    "identifier": "com.example.myapp",
    "build": {
        "devUrl": "http://localhost:5173",
        "frontendDist": "../dist",
        "beforeDevCommand": "npm run dev",
        "beforeBuildCommand": "npm run build"
    },
    "app": {
        "windows": [{
            "label": "main",
            "title": "My App",
            "width": 800,
            "height": 600
        }],
        "security": {
            "csp": "default-src 'self'; img-src 'self' data:",
            "capabilities": ["default"]
        }
    },
    "bundle": {
        "active": true,
        "targets": "all",
        "icon": ["icons/icon.icns", "icons/icon.ico", "icons/icon.png"]
    }
}

关键设置:

  • build.devUrl:必须与前端开发服务器端口一致
  • app.security.capabilities:能力文件标识符数组

插件配置 — 某些插件需要额外的 tauri.conf.json 配置块(例如,store、updater)。请务必查看 v2.tauri.app/plugin/<plugin-name>/ 中相应插件的文档,了解所需的配置键。

项目结构

my-tauri-app/
├── src/                    # Frontend source
├── src-tauri/
│   ├── src/
│   │   ├── main.rs         # Thin passthrough — calls lib::run()
│   │   └── lib.rs          # ALL application logic lives here
│   ├── capabilities/
│   │   └── default.json    # Capability definitions (grant permissions here)
│   ├── tauri.conf.json     # App configuration (devUrl, bundle, security)
│   ├── Cargo.toml          # Rust dependencies
│   └── build.rs            # Build script (required for tauri-build)
└── package.json

为什么由 lib.rs 负责所有逻辑: Tauri 会在移动平台上将 main() 替换为 #[cfg_attr(mobile, tauri::mobile_entry_point)]。所有命令、状态和构建器设置都必须位于 lib.rs::run() 中。

Cargo.toml
[package]
name = "app"
version = "0.1.0"
edition = "2021"

[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]

[build-dependencies]
tauri-build = { version = "2", features = [] }

[dependencies]
tauri = { version = "2", features = [] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

关键设置:

  • [lib] 部分:移动端构建所必需
  • crate-type:必须包含全部三种类型,才能跨平台使用

常见模式

错误处理模式

使用 Result<T, E> 和 thiserror,在 IPC 边界上实现类型安全的错误传递。完整实现细节请参阅 references/ipc-patterns.md。

use thiserror::Error;

#[derive(Debug, Error)]
enum AppError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    #[error("Not found: {0}")]
    NotFound(String),
}

impl serde::Serialize for AppError {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where S: serde::ser::Serializer {
        serializer.serialize_str(self.to_string().as_ref())
    }
}

#[tauri::command]
fn risky_operation() -> Result<String, AppError> {
    Ok("success".into())
}
Serde 边界规则

所有命令参数都必须实现 serde::Deserialize,返回类型必须实现 serde::Serialize。Tauri 通过这种方式在 IPC 边界上传递 JSON。

use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct CreateUserArgs {
    name: String,
    email: String,
    role: Option<String>,  // Optional fields use Option<T>
}

#[derive(Serialize)]
struct User {
    id: u64,
    name: String,
}

#[tauri::command]
fn create_user(args: CreateUserArgs) -> Result<User, String> {
    Ok(User { id: 1, name: args.name })
}

常见的 serde 易错点:

  • 字段名称在 JS 中为 camelCase,在 Rust 中为 snake_case——Tauri 会自动在两者之间转换
  • Option<T> 会映射为可选的 JS 参数(可以是 undefined 或 null)
  • 复杂枚举需要使用 #[serde(tag = "type")] 或类似方式,以确保 JSON 兼容
  • 错误类型也必须实现 Serialize(参见上文的错误处理模式)
状态管理模式

Tauri 状态用于管理各个命令之间的应用程序数据。更复杂的状态模式请参阅 references/ipc-patterns.md。

use std::sync::Mutex;
use tauri::State;

struct AppState {
    counter: u32,
}

#[tauri::command]
fn increment(state: State<'_, Mutex<AppState>>) -> u32 {
    let mut s = state.lock().unwrap();
    s.counter += 1;
    s.counter
}

// In builder:
tauri::Builder::default()
    .manage(Mutex::new(AppState { counter: 0 }))
事件发送模式

事件是即发即弃的通知。双向示例请参阅 references/ipc-patterns.md。

use tauri::Emitter;

#[tauri::command]
fn start_task(app: tauri::AppHandle) {
    std::thread::spawn(move || {
        app.emit("task-progress", 50).unwrap();
        app.emit("task-complete", "done").unwrap();
    });
}
import { listen } from '@tauri-apps/api/event';

const unlisten = await listen('task-progress', (e) => {
    console.log('Progress:', e.payload);
});
// Call unlisten() when done
通道流式传输模式

通道支持从 Rust 到前端的高频类型化流式传输。完整实现细节请参阅 references/ipc-patterns.md。

use tauri::ipc::Channel;

#[derive(Clone, serde::Serialize)]
#[serde(tag = "event", content = "data")]
enum DownloadEvent {
    Progress { percent: u32 },
    Complete { path: String },
}

#[tauri::command]
async fn download(url: String, on_event: Channel<DownloadEvent>) {
    for i in 0..=100 {
        on_event.send(DownloadEvent::Progress { percent: i }).unwrap();
    }
    on_event.send(DownloadEvent::Complete { path: "/downloads/file".into() }).unwrap();
}
import { invoke, Channel } from '@tauri-apps/api/core';

const channel = new Channel<DownloadEvent>();
channel.onmessage = (msg) => console.log(msg.event, msg.data);
await invoke('download', { url: 'https://...', onEvent: channel });
窗口访问模式

Tauri v2 使用 WebviewWindow 统一管理窗口和 WebView。

use tauri::Manager;

#[tauri::command]
fn focus_window(app: tauri::AppHandle) {
    if let Some(window) = app.get_webview_window("main") {
        let _ = window.set_focus();
    }
}

这很重要的原因: 在 v2 中使用 tauri::WebviewWindow 和 app.get_webview_window("label");v1 的 app.get_window() API 已在 v2 中移除。

捆绑资源

参考资料

位于 references/ 中:

注意: 有关特定主题的深入说明,请参阅上述参考文件。

依赖项

必需
软件包版本用途
@tauri-apps/cli^2 (v2+)CLI 工具
@tauri-apps/api^2 (v2+)前端 API
tauri^2 (v2+)Rust 核心
tauri-build^2 (v2+)构建脚本

*最后验证时间:2026-04-02。请务必查阅官方更新日志,确认功能推出时间。

可选(插件)
软件包版本用途关键权限
tauri-plugin-fs^2 (v2+)文件系统访问fs:default
tauri-plugin-dialog^2 (v2+)原生对话框dialog:default
tauri-plugin-shell^2 (v2+)Shell 命令、打开 URLshell:default
tauri-plugin-http^2 (v2+)HTTP 客户端http:default
tauri-plugin-store^2 (v2+)键值存储store:default

插件权限为必需项。 安装插件后,若未将其权限字符串添加到 capability 文件中,会导致运行时故障且不显示错误。有关所有官方插件的完整安装和权限详情,请参阅 references/plugin-reference.md。

官方文档

故障排除

启动时显示白屏

症状: 应用启动,但显示空白白屏

解决方案:

  1. 确认 devUrl 与前端开发服务器端口一致
  2. 检查 beforeDevCommand 是否启动了开发服务器
  3. 打开 DevTools(Cmd+Option+I / Ctrl+Shift+I)检查错误
命令返回 Undefined

症状: invoke() 返回 undefined,而不是预期值

解决方案:

  1. 确认命令已添加到 generate_handler![] 中
  2. 检查 Rust 命令是否确实返回了值
  3. 确保参数名称匹配(默认情况下,JS 使用 camelCase,Rust 使用 snake_case)
移动端构建失败

症状: Android/iOS 构建因缺少目标平台而失败

解决方案:

# Android targets
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android

# iOS targets (macOS only)
rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
桌面端与移动端的行为差异

并非所有 Tauri API 和插件都支持移动端(iOS/Android)。在移动端构建中使用任何插件或 API 之前:

  1. 查看插件页面,在 v2.tauri.app/plugin/<name>/ 查看平台支持矩阵
  2. 常见的仅限桌面端功能:系统托盘(TrayIconBuilder)、窗口标签/多窗口、部分 shell 插件功能
  3. 适用于移动端的模式:IPC 命令/事件/通道可在所有平台上运行;tauri::AppHandle 适用于移动端
  4. 条件编译:使用 #[cfg(desktop)] / #[cfg(mobile)] 编写特定平台的 Rust 逻辑
#[tauri::command]
fn platform_info() -> String {
    #[cfg(desktop)]
    return "desktop".to_string();
    #[cfg(mobile)]
    return "mobile".to_string();
}

设置检查清单

使用此技能前,请确认:

  • npx tauri info 显示正确的 Tauri v2 版本
  • src-tauri/capabilities/default.json 文件存在,且至少包含 core:default
  • 所有命令都已在 generate_handler![] 中注册
  • lib.rs 包含共享代码(用于支持移动端)
  • 已为目标平台安装所需的 Rust 目标

评论

支持 Markdown