tauri-v2

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Tauri v2 Development Skill

Tauri v2 开发技能指南

Build cross-platform desktop and mobile apps with web frontends and Rust backends.
使用Web前端和Rust后端构建跨平台桌面与移动应用。

Before You Start

开始之前

This skill prevents 8+ common errors and saves ~60% tokens.
MetricWithout SkillWith Skill
Setup Time~2 hours~30 min
Common Errors8+0
Token UsageHigh (exploration)Low (direct patterns)
本技能可避免8+常见错误,节省约60%的令牌消耗。
指标未使用本技能使用本技能
搭建时间~2小时~30分钟
常见错误8+0
令牌使用高(探索式)低(直接匹配模式)

Known Issues This Skill Prevents

本技能可预防的已知问题

  1. Permission denied errors from missing capabilities
  2. IPC failures from unregistered commands in
    generate_handler!
  3. State management panics from type mismatches
  4. Mobile build failures from missing Rust targets
  5. White screen issues from misconfigured dev URLs
  1. 因缺少capabilities导致的权限拒绝错误
  2. 因未在
    generate_handler!
    中注册命令导致的IPC失败
  3. 因类型不匹配导致的状态管理 panic
  4. 因缺少Rust目标平台导致的移动构建失败
  5. 因开发URL配置错误导致的白屏问题

Quick Start

快速开始

Step 1: Create a Tauri Command

步骤1:创建Tauri命令

rust
// src-tauri/src/lib.rs
#[tauri::command]
fn greet(name: String) -> String {
    format!("Hello, {}!", name)
}

pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
Why this matters: Commands not in
generate_handler![]
silently fail when invoked from frontend.
rust
// src-tauri/src/lib.rs
#[tauri::command]
fn greet(name: String) -> String {
    format!("Hello, {}!", name)
}

pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
重要性说明: 未加入
generate_handler![]
的命令在从前端调用时会静默失败。

Step 2: Call from Frontend

步骤2:从前端调用

typescript
import { invoke } from '@tauri-apps/api/core';

const greeting = await invoke<string>('greet', { name: 'World' });
console.log(greeting); // "Hello, World!"
Why this matters: Use
@tauri-apps/api/core
(not
@tauri-apps/api/tauri
- that's v1 API).
typescript
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)。

Step 3: Add Required Permissions

步骤3:添加必要权限

json
// src-tauri/capabilities/default.json
{
    "$schema": "../gen/schemas/desktop-schema.json",
    "identifier": "default",
    "windows": ["main"],
    "permissions": ["core:default"]
}
Why this matters: Tauri v2 denies everything by default - explicit permissions required for all operations.
json
// src-tauri/capabilities/default.json
{
    "$schema": "../gen/schemas/desktop-schema.json",
    "identifier": "default",
    "windows": ["main"],
    "permissions": ["core:default"]
}
重要性说明: Tauri v2默认拒绝所有操作——所有功能都需要显式配置权限。

Critical Rules

关键规则

Always Do

必须遵守

  • Register every command in
    tauri::generate_handler![cmd1, cmd2, ...]
  • Return
    Result<T, E>
    from commands for proper error handling
  • Use
    Mutex<T>
    for shared state accessed from multiple commands
  • Add capabilities before using any plugin features
  • Use
    lib.rs
    for shared code (required for mobile builds)
  • 所有命令都要注册到
    tauri::generate_handler![cmd1, cmd2, ...]
  • 命令返回
    Result<T, E>
    以实现正确的错误处理
  • 多命令访问的共享状态使用
    Mutex<T>
  • 使用插件功能前先配置capabilities
  • 共享代码放在
    lib.rs
    中(移动构建必需)

Never Do

严禁操作

  • Never use borrowed types (
    &str
    ) in async commands - use owned types
  • Never block the main thread - use async for I/O operations
  • Never hardcode paths - use Tauri path APIs (
    app.path()
    )
  • Never skip capability setup - even "safe" operations need permissions
  • 异步命令中绝不使用借用类型(
    &str
    )——使用自有类型
  • 绝不阻塞主线程——I/O操作使用异步方式
  • 绝不硬编码路径——使用Tauri路径API(
    app.path()
  • 绝不跳过capabilities配置——即使是“安全”操作也需要权限

Common Mistakes

常见错误示例

Wrong - Borrowed type in async:
rust
#[tauri::command]
async fn bad(name: &str) -> String { // Compile error!
    name.to_string()
}
Correct - Owned type:
rust
#[tauri::command]
async fn good(name: String) -> String {
    name
}
Why: Async commands cannot borrow data across await points; Tauri requires owned types for async command parameters.
错误写法 - 异步命令中使用借用类型:
rust
#[tauri::command]
async fn bad(name: &str) -> String { // 编译错误!
    name.to_string()
}
正确写法 - 使用自有类型:
rust
#[tauri::command]
async fn good(name: String) -> String {
    name
}
原因: 异步命令无法在await点之间借用数据;Tauri要求异步命令的参数必须是自有类型。

Known Issues Prevention

已知问题预防方案

IssueRoot CauseSolution
"Command not found"Missing from
generate_handler!
Add command to handler macro
"Permission denied"Missing capabilityAdd to
capabilities/default.json
State panic on accessType mismatch in
State<T>
Use exact type from
.manage()
White screen on launchFrontend not buildingCheck
beforeDevCommand
in config
IPC timeoutBlocking async commandRemove blocking code or use spawn
Mobile build failsMissing Rust targetsRun
rustup target add <target>
问题根因解决方案
"Command not found"(命令未找到)未加入
generate_handler!
将命令添加到处理宏中
"Permission denied"(权限拒绝)缺少capability添加到
capabilities/default.json
状态访问时panic
State<T>
类型不匹配
使用
.manage()
中定义的精确类型
启动时白屏前端未构建完成检查配置中的
beforeDevCommand
IPC超时异步命令阻塞移除阻塞代码或使用spawn
移动构建失败缺少Rust目标平台运行
rustup target add <target>

Configuration Reference

配置参考

tauri.conf.json

tauri.conf.json

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"]
    }
}
Key settings:
  • build.devUrl
    : Must match your frontend dev server port
  • app.security.capabilities
    : Array of capability file identifiers
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
    :capability文件标识符数组

Cargo.toml

Cargo.toml

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"
Key settings:
  • [lib]
    section: Required for mobile builds
  • crate-type
    : Must include all three types for cross-platform
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
    :必须包含三种类型以支持跨平台

Common Patterns

常见模式

Error Handling Pattern

错误处理模式

rust
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())
}
rust
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())
}

State Management Pattern

状态管理模式

rust
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 }))
rust
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
}

// 在构建器中配置:
tauri::Builder::default()
    .manage(Mutex::new(AppState { counter: 0 }))

Event Emission Pattern

事件发送模式

rust
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();
    });
}
typescript
import { listen } from '@tauri-apps/api/event';

const unlisten = await listen('task-progress', (e) => {
    console.log('Progress:', e.payload);
});
// Call unlisten() when done
rust
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();
    });
}
typescript
import { listen } from '@tauri-apps/api/event';

const unlisten = await listen('task-progress', (e) => {
    console.log('Progress:', e.payload);
});
// 完成后调用unlisten()

Channel Streaming Pattern

通道流模式

rust
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();
}
typescript
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 });
rust
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();
}
typescript
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 });

Bundled Resources

内置资源

References

参考文档

Located in
references/
:
  • capabilities-reference.md
    - Permission patterns and examples
  • ipc-patterns.md
    - Complete IPC examples
Note: For deep dives on specific topics, see the reference files above.
位于
references/
目录下:
  • capabilities-reference.md
    - 权限模式与示例
  • ipc-patterns.md
    - 完整IPC示例
注意: 如需深入了解特定主题,请查阅上述参考文档。

Dependencies

依赖项

Required

必需依赖

PackageVersionPurpose
@tauri-apps/cli
^2.0.0CLI tooling
@tauri-apps/api
^2.0.0Frontend APIs
tauri
^2.0.0Rust core
tauri-build
^2.0.0Build scripts
版本用途
@tauri-apps/cli
^2.0.0CLI工具链
@tauri-apps/api
^2.0.0前端API
tauri
^2.0.0Rust核心库
tauri-build
^2.0.0构建脚本

Optional (Plugins)

可选依赖(插件)

PackageVersionPurpose
tauri-plugin-fs
^2.0.0File system access
tauri-plugin-dialog
^2.0.0Native dialogs
tauri-plugin-shell
^2.0.0Shell commands, open URLs
tauri-plugin-http
^2.0.0HTTP client
tauri-plugin-store
^2.0.0Key-value storage
版本用途
tauri-plugin-fs
^2.0.0文件系统访问
tauri-plugin-dialog
^2.0.0原生对话框
tauri-plugin-shell
^2.0.0Shell命令、URL打开
tauri-plugin-http
^2.0.0HTTP客户端
tauri-plugin-store
^2.0.0键值存储

Official Documentation

官方文档

Troubleshooting

故障排查

White Screen on Launch

启动时白屏

Symptoms: App launches but shows blank white screen
Solution:
  1. Verify
    devUrl
    matches your frontend dev server port
  2. Check
    beforeDevCommand
    runs your dev server
  3. Open DevTools (Cmd+Option+I / Ctrl+Shift+I) to check for errors
症状: 应用启动后显示空白白屏
解决方案:
  1. 验证
    devUrl
    与前端开发服务器端口匹配
  2. 检查
    beforeDevCommand
    是否正确启动开发服务器
  3. 打开开发者工具(Cmd+Option+I / Ctrl+Shift+I)查看错误信息

Command Returns Undefined

命令返回Undefined

Symptoms:
invoke()
returns undefined instead of expected value
Solution:
  1. Verify command is in
    generate_handler![]
  2. Check Rust command actually returns a value
  3. Ensure argument names match (camelCase in JS, snake_case in Rust by default)
症状:
invoke()
返回undefined而非预期值
解决方案:
  1. 验证命令已注册到
    generate_handler![]
  2. 检查Rust命令确实返回了值
  3. 确保参数名称匹配(JS中使用驼峰式,Rust默认使用蛇形命名)

Mobile Build Failures

移动构建失败

Symptoms: Android/iOS build fails with missing target
Solution:
bash
undefined
症状: Android/iOS构建因缺少目标平台失败
解决方案:
bash
undefined

Android targets

Android目标平台

rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android

iOS targets (macOS only)

iOS目标平台(仅支持macOS)

rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
undefined
rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
undefined

Setup Checklist

搭建检查清单

Before using this skill, verify:
  • npx tauri info
    shows correct Tauri v2 versions
  • src-tauri/capabilities/default.json
    exists with at least
    core:default
  • All commands registered in
    generate_handler![]
  • lib.rs
    contains shared code (for mobile support)
  • Required Rust targets installed for target platforms
使用本技能前,请验证:
  • npx tauri info
    显示正确的Tauri v2版本
  • src-tauri/capabilities/default.json
    已存在且至少包含
    core:default
  • 所有命令已注册到
    generate_handler![]
  • lib.rs
    包含共享代码(用于支持移动平台)
  • 已安装目标平台所需的Rust目标架构