tauri-v2
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTauri 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.
| Metric | Without Skill | With Skill |
|---|---|---|
| Setup Time | ~2 hours | ~30 min |
| Common Errors | 8+ | 0 |
| Token Usage | High (exploration) | Low (direct patterns) |
本技能可避免8+常见错误,节省约60%的令牌消耗。
| 指标 | 未使用本技能 | 使用本技能 |
|---|---|---|
| 搭建时间 | ~2小时 | ~30分钟 |
| 常见错误 | 8+ | 0 |
| 令牌使用 | 高(探索式) | 低(直接匹配模式) |
Known Issues This Skill Prevents
本技能可预防的已知问题
- Permission denied errors from missing capabilities
- IPC failures from unregistered commands in
generate_handler! - State management panics from type mismatches
- Mobile build failures from missing Rust targets
- White screen issues from misconfigured dev URLs
- 因缺少capabilities导致的权限拒绝错误
- 因未在中注册命令导致的IPC失败
generate_handler! - 因类型不匹配导致的状态管理 panic
- 因缺少Rust目标平台导致的移动构建失败
- 因开发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 silently fail when invoked from frontend.
generate_handler![]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 (not - that's v1 API).
@tauri-apps/api/core@tauri-apps/api/tauritypescript
import { invoke } from '@tauri-apps/api/core';
const greeting = await invoke<string>('greet', { name: 'World' });
console.log(greeting); // "Hello, World!"重要性说明: 请使用(不要使用——这是v1版本的API)。
@tauri-apps/api/core@tauri-apps/api/tauriStep 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 from commands for proper error handling
Result<T, E> - Use for shared state accessed from multiple commands
Mutex<T> - Add capabilities before using any plugin features
- Use for shared code (required for mobile builds)
lib.rs
- 所有命令都要注册到中
tauri::generate_handler![cmd1, cmd2, ...] - 命令返回以实现正确的错误处理
Result<T, E> - 多命令访问的共享状态使用
Mutex<T> - 使用插件功能前先配置capabilities
- 共享代码放在中(移动构建必需)
lib.rs
Never Do
严禁操作
- Never use borrowed types () in async commands - use owned types
&str - 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
已知问题预防方案
| Issue | Root Cause | Solution |
|---|---|---|
| "Command not found" | Missing from | Add command to handler macro |
| "Permission denied" | Missing capability | Add to |
| State panic on access | Type mismatch in | Use exact type from |
| White screen on launch | Frontend not building | Check |
| IPC timeout | Blocking async command | Remove blocking code or use spawn |
| Mobile build fails | Missing Rust targets | Run |
| 问题 | 根因 | 解决方案 |
|---|---|---|
| "Command not found"(命令未找到) | 未加入 | 将命令添加到处理宏中 |
| "Permission denied"(权限拒绝) | 缺少capability | 添加到 |
| 状态访问时panic | | 使用 |
| 启动时白屏 | 前端未构建完成 | 检查配置中的 |
| IPC超时 | 异步命令阻塞 | 移除阻塞代码或使用spawn |
| 移动构建失败 | 缺少Rust目标平台 | 运行 |
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:
- : Must match your frontend dev server port
build.devUrl - : Array of capability file identifiers
app.security.capabilities
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 - :capability文件标识符数组
app.security.capabilities
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:
- section: Required for mobile builds
[lib] - : Must include all three types for cross-platform
crate-type
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 donerust
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/- - Permission patterns and examples
capabilities-reference.md - - Complete IPC examples
ipc-patterns.md
Note: For deep dives on specific topics, see the reference files above.
位于目录下:
references/- - 权限模式与示例
capabilities-reference.md - - 完整IPC示例
ipc-patterns.md
注意: 如需深入了解特定主题,请查阅上述参考文档。
Dependencies
依赖项
Required
必需依赖
| Package | Version | Purpose |
|---|---|---|
| ^2.0.0 | CLI tooling |
| ^2.0.0 | Frontend APIs |
| ^2.0.0 | Rust core |
| ^2.0.0 | Build scripts |
| 包 | 版本 | 用途 |
|---|---|---|
| ^2.0.0 | CLI工具链 |
| ^2.0.0 | 前端API |
| ^2.0.0 | Rust核心库 |
| ^2.0.0 | 构建脚本 |
Optional (Plugins)
可选依赖(插件)
| Package | Version | Purpose |
|---|---|---|
| ^2.0.0 | File system access |
| ^2.0.0 | Native dialogs |
| ^2.0.0 | Shell commands, open URLs |
| ^2.0.0 | HTTP client |
| ^2.0.0 | Key-value storage |
| 包 | 版本 | 用途 |
|---|---|---|
| ^2.0.0 | 文件系统访问 |
| ^2.0.0 | 原生对话框 |
| ^2.0.0 | Shell命令、URL打开 |
| ^2.0.0 | HTTP客户端 |
| ^2.0.0 | 键值存储 |
Official Documentation
官方文档
Troubleshooting
故障排查
White Screen on Launch
启动时白屏
Symptoms: App launches but shows blank white screen
Solution:
- Verify matches your frontend dev server port
devUrl - Check runs your dev server
beforeDevCommand - Open DevTools (Cmd+Option+I / Ctrl+Shift+I) to check for errors
症状: 应用启动后显示空白白屏
解决方案:
- 验证与前端开发服务器端口匹配
devUrl - 检查是否正确启动开发服务器
beforeDevCommand - 打开开发者工具(Cmd+Option+I / Ctrl+Shift+I)查看错误信息
Command Returns Undefined
命令返回Undefined
Symptoms: returns undefined instead of expected value
invoke()Solution:
- Verify command is in
generate_handler![] - Check Rust command actually returns a value
- Ensure argument names match (camelCase in JS, snake_case in Rust by default)
症状: 返回undefined而非预期值
invoke()解决方案:
- 验证命令已注册到中
generate_handler![] - 检查Rust命令确实返回了值
- 确保参数名称匹配(JS中使用驼峰式,Rust默认使用蛇形命名)
Mobile Build Failures
移动构建失败
Symptoms: Android/iOS build fails with missing target
Solution:
bash
undefined症状: Android/iOS构建因缺少目标平台失败
解决方案:
bash
undefinedAndroid 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
undefinedrustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
undefinedSetup Checklist
搭建检查清单
Before using this skill, verify:
- shows correct Tauri v2 versions
npx tauri info - exists with at least
src-tauri/capabilities/default.jsoncore:default - All commands registered in
generate_handler![] - contains shared code (for mobile support)
lib.rs - Required Rust targets installed for target platforms
使用本技能前,请验证:
- 显示正确的Tauri v2版本
npx tauri info - 已存在且至少包含
src-tauri/capabilities/default.jsoncore:default - 所有命令已注册到中
generate_handler![] - 包含共享代码(用于支持移动平台)
lib.rs - 已安装目标平台所需的Rust目标架构