第一个应用 #

一、创建项目 #

1.1 使用脚手架创建 #

bash
# 创建新项目
npm create tauri-app@latest my-first-app

# 进入项目目录
cd my-first-app

1.2 选择配置 #

text
✔ Project name · my-first-app
✔ Choose which language to use for your frontend · TypeScript / JavaScript
✔ Choose your package manager · pnpm
✔ Choose your UI template · React
✔ Choose your UI flavor · TypeScript

1.3 安装依赖 #

bash
# 安装前端依赖
pnpm install

二、项目结构 #

2.1 目录概览 #

text
my-first-app/
├── src/                    # 前端源码
│   ├── App.tsx            # 主组件
│   ├── main.tsx           # 入口文件
│   └── App.css            # 样式文件
├── src-tauri/             # Tauri 后端
│   ├── src/
│   │   ├── main.rs        # 主入口
│   │   └── lib.rs         # 库文件
│   ├── Cargo.toml         # Rust 配置
│   └── tauri.conf.json    # Tauri 配置
├── index.html             # HTML 模板
├── package.json           # 项目配置
└── vite.config.ts         # Vite 配置

2.2 关键文件 #

文件 作用
src-tauri/src/lib.rs Rust 后端逻辑
src/App.tsx 前端主组件
tauri.conf.json Tauri 配置

三、编写后端代码 #

3.1 修改 lib.rs #

编辑 src-tauri/src/lib.rs

rust
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! Welcome to Tauri!", name)
}

#[tauri::command]
fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[tauri::command]
fn get_system_info() -> SystemInfo {
    SystemInfo {
        os: std::env::consts::OS.to_string(),
        arch: std::env::consts::ARCH.to_string(),
    }
}

#[derive(serde::Serialize)]
struct SystemInfo {
    os: String,
    arch: String,
}

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

3.2 命令详解 #

注解/函数 说明
#[tauri::command] 标记函数为可调用命令
invoke_handler 注册命令处理器
generate_handler! 生成命令处理代码

3.3 命令参数类型 #

rust
// 基本类型
#[tauri::command]
fn basic_types(
    string_param: String,
    int_param: i32,
    float_param: f64,
    bool_param: bool,
) -> String {
    format!("Received: {}, {}, {}, {}", string_param, int_param, float_param, bool_param)
}

// 可选参数
#[tauri::command]
fn optional_param(name: Option<String>) -> String {
    match name {
        Some(n) => format!("Hello, {}!", n),
        None => "Hello, stranger!".to_string(),
    }
}

// 结构体参数
#[derive(serde::Deserialize)]
struct User {
    name: String,
    age: u32,
}

#[tauri::command]
fn struct_param(user: User) -> String {
    format!("User: {}, age: {}", user.name, user.age)
}

// 返回 Result
#[tauri::command]
fn may_fail(input: i32) -> Result<String, String> {
    if input > 0 {
        Ok(format!("Success: {}", input))
    } else {
        Err("Input must be positive".to_string())
    }
}

四、编写前端代码 #

4.1 修改 App.tsx #

编辑 src/App.tsx

tsx
import { useState } from 'react';
import { invoke } from '@tauri-apps/api/core';
import './App.css';

interface SystemInfo {
    os: string;
    arch: string;
}

function App() {
    const [name, setName] = useState('');
    const [greeting, setGreeting] = useState('');
    const [sum, setSum] = useState<number | null>(null);
    const [systemInfo, setSystemInfo] = useState<SystemInfo | null>(null);

    const handleGreet = async () => {
        try {
            const result = await invoke<string>('greet', { name });
            setGreeting(result);
        } catch (error) {
            console.error('Error:', error);
        }
    };

    const handleAdd = async () => {
        try {
            const result = await invoke<number>('add', { a: 10, b: 20 });
            setSum(result);
        } catch (error) {
            console.error('Error:', error);
        }
    };

    const handleGetSystemInfo = async () => {
        try {
            const info = await invoke<SystemInfo>('get_system_info');
            setSystemInfo(info);
        } catch (error) {
            console.error('Error:', error);
        }
    };

    return (
        <div className="container">
            <h1>我的第一个 Tauri 应用</h1>

            <div className="section">
                <h2>问候功能</h2>
                <input
                    type="text"
                    value={name}
                    onChange={(e) => setName(e.target.value)}
                    placeholder="输入你的名字"
                />
                <button onClick={handleGreet}>问候</button>
                {greeting && <p className="result">{greeting}</p>}
            </div>

            <div className="section">
                <h2>计算功能</h2>
                <button onClick={handleAdd}>计算 10 + 20</button>
                {sum !== null && <p className="result">结果: {sum}</p>}
            </div>

            <div className="section">
                <h2>系统信息</h2>
                <button onClick={handleGetSystemInfo}>获取系统信息</button>
                {systemInfo && (
                    <div className="result">
                        <p>操作系统: {systemInfo.os}</p>
                        <p>架构: {systemInfo.arch}</p>
                    </div>
                )}
            </div>
        </div>
    );
}

export default App;

4.2 添加样式 #

编辑 src/App.css

css
.container {
    max-width: 600px;
    margin: 0 auto;
    padding: 20px;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

h1 {
    color: #333;
    text-align: center;
    margin-bottom: 30px;
}

.section {
    background: #f5f5f5;
    padding: 20px;
    border-radius: 8px;
    margin-bottom: 20px;
}

h2 {
    color: #666;
    font-size: 18px;
    margin-bottom: 15px;
}

input {
    width: 100%;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
    margin-bottom: 10px;
    font-size: 14px;
}

button {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    border: none;
    padding: 10px 20px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    transition: transform 0.2s, box-shadow 0.2s;
}

button:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}

.result {
    margin-top: 15px;
    padding: 10px;
    background: #fff;
    border-radius: 4px;
    border-left: 4px solid #667eea;
}

五、配置文件 #

5.1 tauri.conf.json #

json
{
    "$schema": "https://schema.tauri.app/config/2",
    "productName": "my-first-app",
    "version": "0.1.0",
    "identifier": "com.example.my-first-app",
    "build": {
        "beforeDevCommand": "pnpm dev",
        "devUrl": "http://localhost:1420",
        "beforeBuildCommand": "pnpm build",
        "frontendDist": "../dist"
    },
    "app": {
        "withGlobalTauri": true,
        "windows": [
            {
                "title": "我的第一个 Tauri 应用",
                "width": 800,
                "height": 600,
                "center": true,
                "resizable": true
            }
        ]
    },
    "bundle": {
        "active": true,
        "targets": "all",
        "icon": [
            "icons/32x32.png",
            "icons/128x128.png",
            "icons/128x128@2x.png",
            "icons/icon.icns",
            "icons/icon.ico"
        ]
    }
}

5.2 Cargo.toml #

toml
[package]
name = "my-first-app"
version = "0.1.0"
edition = "2021"

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

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

[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]

六、运行应用 #

6.1 开发模式 #

bash
# 启动开发服务器
pnpm tauri dev

6.2 预期效果 #

text
┌────────────────────────────────────────┐
│  我的第一个 Tauri 应用           ─ □ × │
├────────────────────────────────────────┤
│                                        │
│           我的第一个 Tauri 应用         │
│                                        │
│  ┌────────────────────────────────┐   │
│  │ 问候功能                        │   │
│  │ [输入你的名字        ]          │   │
│  │ [问候]                          │   │
│  │ Hello, World! Welcome to Tauri! │   │
│  └────────────────────────────────┘   │
│                                        │
│  ┌────────────────────────────────┐   │
│  │ 计算功能                        │   │
│  │ [计算 10 + 20]                  │   │
│  │ 结果: 30                        │   │
│  └────────────────────────────────┘   │
│                                        │
│  ┌────────────────────────────────┐   │
│  │ 系统信息                        │   │
│  │ [获取系统信息]                  │   │
│  │ 操作系统: macos                 │   │
│  │ 架构: aarch64                   │   │
│  └────────────────────────────────┘   │
│                                        │
└────────────────────────────────────────┘

七、调试技巧 #

7.1 前端调试 #

typescript
// 使用 console.log
console.log('Debug info:', data);

// 打开开发者工具
// Windows/Linux: Ctrl + Shift + I
// macOS: Cmd + Option + I

7.2 后端调试 #

rust
// 使用 println!
println!("Debug: {:?}", data);

// 使用 log crate
use log::{info, debug, error};

#[tauri::command]
fn debug_command() {
    info!("This is an info message");
    debug!("This is a debug message");
    error!("This is an error message");
}

7.3 VS Code 调试 #

json
// .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "request": "launch",
            "name": "Debug Tauri App",
            "cargo": {
                "args": [
                    "build",
                    "--manifest-path=./src-tauri/Cargo.toml"
                ]
            },
            "preLaunchTask": "ui:dev"
        }
    ]
}

八、错误处理 #

8.1 前端错误处理 #

typescript
try {
    const result = await invoke<string>('greet', { name });
    setGreeting(result);
} catch (error) {
    console.error('Command failed:', error);
    alert(`Error: ${error}`);
}

8.2 后端错误处理 #

rust
#[tauri::command]
fn safe_divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err("Division by zero".to_string())
    } else {
        Ok(a / b)
    }
}

九、常见问题 #

9.1 命令未找到 #

rust
// 确保命令已注册
.invoke_handler(tauri::generate_handler![
    greet,  // 确保这里包含了你的命令
    add,
    get_system_info
])

9.2 类型不匹配 #

rust
// 确保添加 Serialize/Deserialize
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct MyData {
    field: String,
}

9.3 跨域问题 #

json
// tauri.conf.json
{
    "app": {
        "security": {
            "csp": "default-src 'self'; script-src 'self' 'unsafe-inline'"
        }
    }
}

十、总结 #

10.1 核心要点 #

要点 说明
命令定义 使用 #[tauri::command] 注解
命令注册 invoke_handler 中注册
前端调用 使用 invoke() 函数
类型支持 需要实现 Serialize/Deserialize

10.2 下一步 #

现在你已经创建了第一个 Tauri 应用,接下来让我们学习 项目结构,了解如何组织一个规范的 Tauri 项目!

最后更新:2026-03-28