关于 tauri-v2
tauri-v2 是面向 Tauri v2+ 开发者的开发技能,提供桌面和移动应用构建指导。内容聚焦 Rust 与 Web 前端交互、权限配置及常见构建和运行问题。
主要功能
- 01Tauri v2 命令与 IPC 模式
- 02能力与权限配置指导
- 03Rust 状态管理和错误处理
- 04桌面与移动构建注意事项
- 05常见问题排查参考
适用场景
- 01搭建 Tauri v2 应用结构
- 02排查命令调用或权限错误
- 03准备移动端构建配置
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ nodnarbnitram/ claude-code-extensions --skill tauri-v2安装后在 Claude Code 中输入/tauri-v2确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdreferences/advanced-runtime-reference.mdcapabilities-reference.mdipc-patterns.mdplugin-reference.mdREADME.mdupdater-distribution-reference.md详细介绍
Tauri v2+ 开发技能
使用 Web 前端和 Rust 后端构建跨平台桌面与移动应用。
开始之前
此技能可避免 8 个以上的常见错误,并节省约 60% 的 Token。
| 指标 | 不使用技能 | 使用技能 |
|---|---|---|
| 设置时间 | 约 2 小时 | 约 30 分钟 |
| 常见错误 | 8 个以上 | 0 |
| Token 用量 | 高(探索) | 低(直接使用模式) |
此技能可避免的已知问题
- 因缺少 capabilities 而出现权限被拒绝错误
- 因未在
generate_handler!中注册命令而导致 IPC 失败 - 因类型不匹配而导致状态管理 panic
- 因缺少 Rust 目标而导致移动端构建失败
- 因开发 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 端点 |
| 找不到 Sidecar | tauri.conf.json 中没有 externalBin,或缺少可执行文件 | 将路径添加到 bundle.externalBin,并确保二进制文件已打包 |
| 功能在桌面端正常,在移动端失效 | 使用了仅限桌面端的 API | 检查 API 是否支持移动端——部分插件仅适用于桌面端 |
| 访问状态时 panic | State<T> 中的类型不匹配 | 使用 .manage() 中的确切类型 |
| 启动时白屏 | 前端未构建 | 检查配置中的 beforeDevCommand |
| IPC 超时 | 异步命令阻塞 | 移除阻塞代码或使用 spawn |
| 移动端构建失败 | 缺少 Rust 目标 | 运行 rustup target add <target> |
深入参考
- 安全与权限 →
references/capabilities-reference.md - IPC 决策指南 →
references/ipc-patterns.md - 官方插件 →
references/plugin-reference.md - 更新程序与分发 →
references/updater-distribution-reference.md - 托盘、sidecar、深层链接 →
references/advanced-runtime-reference.md
配置参考
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/ 中:
capabilities-reference.md- 权限模式和示例ipc-patterns.md- 完整的 IPC 示例plugin-reference.md- 官方插件的安装、注册和权限字符串updater-distribution-reference.md- 签名、HTTPS 要求和应用捆绑分发advanced-runtime-reference.md-TrayIconBuilder、sidecar、深层链接和资源协议
注意: 有关特定主题的深入说明,请参阅上述参考文件。
依赖项
必需
| 软件包 | 版本 | 用途 |
|---|---|---|
@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 命令、打开 URL | shell:default |
tauri-plugin-http | ^2 (v2+) | HTTP 客户端 | http:default |
tauri-plugin-store | ^2 (v2+) | 键值存储 | store:default |
插件权限为必需项。 安装插件后,若未将其权限字符串添加到 capability 文件中,会导致运行时故障且不显示错误。有关所有官方插件的完整安装和权限详情,请参阅
references/plugin-reference.md。
官方文档
故障排除
启动时显示白屏
症状: 应用启动,但显示空白白屏
解决方案:
- 确认
devUrl与前端开发服务器端口一致 - 检查
beforeDevCommand是否启动了开发服务器 - 打开 DevTools(Cmd+Option+I / Ctrl+Shift+I)检查错误
命令返回 Undefined
症状: invoke() 返回 undefined,而不是预期值
解决方案:
- 确认命令已添加到
generate_handler![]中 - 检查 Rust 命令是否确实返回了值
- 确保参数名称匹配(默认情况下,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 之前:
- 查看插件页面,在
v2.tauri.app/plugin/<name>/查看平台支持矩阵 - 常见的仅限桌面端功能:系统托盘(
TrayIconBuilder)、窗口标签/多窗口、部分 shell 插件功能 - 适用于移动端的模式:IPC 命令/事件/通道可在所有平台上运行;
tauri::AppHandle适用于移动端 - 条件编译:使用
#[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 目标