Skip to content

Tauri 快速开始 - 15 分钟创建第一个应用

前言

环境搭建完成后,现在是时候动手创建你的第一个 Tauri 应用了!这篇文章会带你从零开始,15 分钟内创建一个完整的 Tauri 应用,包括前后端通信、文件操作等基本功能。

我还记得第一次创建 Tauri 应用时的兴奋感,当看到只有几 MB 的安装包,启动速度飞快的应用窗口,真的有种"相见恨晚"的感觉。今天,我也希望能把这份体验传递给你。

让我们开始吧!

创建项目

方式一:使用 create-tauri-app(推荐)

这是最简单快捷的方式:

bash
# 使用 npm
npm create tauri-app@latest

# 使用 yarn
yarn create tauri-app

# 使用 pnpm
pnpm create tauri-app

交互式配置

运行命令后,会看到一系列提示:

bash
 Project name · my-tauri-app
 Choose which language to use for your frontend · TypeScript / JavaScript
 Choose your package manager · npm
 Choose your UI template · React
 Choose your UI flavor · TypeScript

让我们逐一解释这些选项:

1. Project name(项目名称)

bash
 Project name · my-tauri-app

# 建议:
- 使用小写字母
- 用连字符分隔单词
- 避免空格和特殊字符

2. Frontend Language(前端语言)

bash
 Choose which language to use for your frontend
 TypeScript / JavaScript

# TypeScript: 类型安全,大型项目推荐
# JavaScript: 简单直接,快速原型推荐

3. Package Manager(包管理器)

bash
 Choose your package manager
 npm
 yarn
 pnpm

# npm: 最通用,Node.js 自带
# yarn: 速度快,离线支持好
# pnpm: 磁盘空间利用率高,速度快

4. UI Template(前端框架)

bash
 Choose your UI template
 Vanilla    # 原生 HTML/CSS/JS
 Vue        # Vue 3
 Svelte     # Svelte
 React      # React
 Solid      # SolidJS
 Angular    # Angular

# 选择你最熟悉的框架
# 如果不确定,推荐 React 或 Vue

我的推荐配置

对于初学者,我推荐这样配置:

bash
Project name: my-first-tauri-app
Frontend: TypeScript / JavaScript TypeScript
Package Manager: npm
UI Template: React
UI Flavor: TypeScript

项目结构详解

创建完成后,进入项目目录:

bash
cd my-first-tauri-app

让我们看看项目结构:

my-first-tauri-app/
├── node_modules/           # Node.js 依赖
├── public/                 # 静态资源
│   └── vite.svg           # 示例图标
├── src/                    # 前端源代码
│   ├── App.tsx            # 主组件
│   ├── main.tsx           # 入口文件
│   └── styles.css         # 样式文件
├── src-tauri/              # Tauri 后端(Rust)
│   ├── icons/             # 应用图标(各种尺寸)
│   ├── src/               # Rust 源代码
│   │   └── main.rs       # Rust 主文件
│   ├── target/            # Rust 编译输出(首次运行后生成)
│   ├── build.rs           # 构建脚本
│   ├── Cargo.toml         # Rust 依赖配置
│   └── tauri.conf.json    # Tauri 配置文件
├── index.html             # HTML 入口
├── package.json           # Node.js 配置
├── tsconfig.json          # TypeScript 配置
└── vite.config.ts         # Vite 配置

关键文件解析

1. src-tauri/tauri.conf.json - Tauri 配置文件

json
{
  "build": {
    "beforeDevCommand": "npm run dev",        // 开发前运行的命令
    "beforeBuildCommand": "npm run build",    // 打包前运行的命令
    "devPath": "http://localhost:1420",       // 开发服务器地址
    "distDir": "../dist"                      // 打包输出目录
  },
  "package": {
    "productName": "my-first-tauri-app",      // 应用名称
    "version": "0.0.0"                        // 应用版本
  },
  "tauri": {
    "allowlist": {                            // API 权限配置
      "all": false,                           // 默认禁用所有 API
      "shell": {
        "all": false
      }
    },
    "windows": [                              // 窗口配置
      {
        "title": "My First Tauri App",        // 窗口标题
        "width": 800,                         // 窗口宽度
        "height": 600                         // 窗口高度
      }
    ]
  }
}

2. src-tauri/src/main.rs - Rust 主文件

rust
// Prevents additional console window on Windows in release
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

// 定义一个简单的命令
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])  // 注册命令
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

3. src/App.tsx - React 主组件

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

function App() {
  const [greetMsg, setGreetMsg] = useState("");
  const [name, setName] = useState("");

  async function greet() {
    // 调用 Rust 命令
    setGreetMsg(await invoke("greet", { name }));
  }

  return (
    <div className="container">
      <h1>Welcome to Tauri!</h1>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          greet();
        }}
      >
        <input
          onChange={(e) => setName(e.currentTarget.value)}
          placeholder="Enter a name..."
        />
        <button type="submit">Greet</button>
      </form>
      <p>{greetMsg}</p>
    </div>
  );
}

export default App;

运行开发服务器

安装依赖

bash
# 安装前端依赖
npm install

启动开发模式

bash
npm run tauri dev

第一次运行会发生什么?

bash
1. 🔧 编译 Rust 后端
   - 下载 Rust 依赖(可能几百个 crate)
   - 编译所有依赖
   - 这一步可能需要 5-10 分钟(仅第一次)
   
2. 🚀 启动前端开发服务器
   - Vite 启动,通常很快
   
3. 🎉 打开应用窗口
   - 如果一切顺利,会看到应用窗口

期望结果

   Compiling tauri v1.5.4
   Compiling my-first-tauri-app v0.0.0
    Finished dev [unoptimized + debuginfo] target(s) in 2.34s
    
    VITE v5.0.8  ready in 234 ms
    
    ➜  Local:   http://localhost:1420/
    ➜  press h to show help

然后,应用窗口会自动打开!

测试功能

  1. 在输入框中输入你的名字
  2. 点击 "Greet" 按钮
  3. 你会看到来自 Rust 的问候消息

🎉 恭喜!你的第一个 Tauri 应用已经运行起来了!

添加自定义功能

现在让我们添加一些实用的功能,深入了解 Tauri 的工作原理。

功能一:读取系统信息

1. 修改 Rust 代码

编辑 src-tauri/src/main.rs

rust
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use std::env;

#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

// 新增:获取系统信息
#[tauri::command]
fn get_system_info() -> String {
    let os = env::consts::OS;
    let arch = env::consts::ARCH;
    let family = env::consts::FAMILY;
    
    format!(
        "操作系统: {}\n架构: {}\n系统家族: {}",
        os, arch, family
    )
}

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
            greet,
            get_system_info  // 注册新命令
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

2. 修改前端代码

编辑 src/App.tsx

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

function App() {
  const [greetMsg, setGreetMsg] = useState("");
  const [name, setName] = useState("");
  const [systemInfo, setSystemInfo] = useState("");

  async function greet() {
    setGreetMsg(await invoke("greet", { name }));
  }

  // 新增:获取系统信息
  async function getSystemInfo() {
    const info = await invoke<string>("get_system_info");
    setSystemInfo(info);
  }

  return (
    <div className="container">
      <h1>Welcome to Tauri!</h1>
      
      {/* 原有的问候功能 */}
      <div className="section">
        <h2>Greet</h2>
        <form
          onSubmit={(e) => {
            e.preventDefault();
            greet();
          }}
        >
          <input
            onChange={(e) => setName(e.currentTarget.value)}
            placeholder="Enter a name..."
          />
          <button type="submit">Greet</button>
        </form>
        {greetMsg && <p className="result">{greetMsg}</p>}
      </div>

      {/* 新增:系统信息 */}
      <div className="section">
        <h2>System Info</h2>
        <button onClick={getSystemInfo}>
          Get System Info
        </button>
        {systemInfo && (
          <pre className="result">{systemInfo}</pre>
        )}
      </div>
    </div>
  );
}

export default App;

3. 添加样式

编辑 src/App.css

css
.container {
  margin: 0;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 30px;
}

.section {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  padding: 20px;
  background: #f9f9f9;
}

.section h2 {
  margin-top: 0;
  color: #333;
}

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

button {
  padding: 10px 20px;
  background: #0070f3;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

button:hover {
  background: #0051cc;
}

.result {
  margin-top: 15px;
  padding: 15px;
  background: white;
  border-radius: 4px;
  border-left: 3px solid #0070f3;
}

pre.result {
  white-space: pre-wrap;
  font-family: monospace;
  font-size: 13px;
}

保存文件后,应用会自动重新加载(热重载)!

功能二:文件选择和读取

1. 配置权限

编辑 src-tauri/tauri.conf.json

json
{
  "tauri": {
    "allowlist": {
      "all": false,
      "fs": {
        "all": false,
        "readFile": true,
        "scope": ["$DESKTOP/*", "$DOCUMENT/*", "$DOWNLOAD/*"]
      },
      "dialog": {
        "all": false,
        "open": true
      }
    }
  }
}

2. 添加前端功能

编辑 src/App.tsx,添加文件选择功能:

tsx
import { useState } from "react";
import { invoke } from "@tauri-apps/api/tauri";
import { open } from "@tauri-apps/api/dialog";
import { readTextFile } from "@tauri-apps/api/fs";
import "./App.css";

function App() {
  const [greetMsg, setGreetMsg] = useState("");
  const [name, setName] = useState("");
  const [systemInfo, setSystemInfo] = useState("");
  const [fileContent, setFileContent] = useState("");

  async function greet() {
    setGreetMsg(await invoke("greet", { name }));
  }

  async function getSystemInfo() {
    const info = await invoke<string>("get_system_info");
    setSystemInfo(info);
  }

  // 新增:选择并读取文件
  async function selectAndReadFile() {
    try {
      // 打开文件选择对话框
      const selected = await open({
        multiple: false,
        filters: [{
          name: 'Text',
          extensions: ['txt', 'md', 'json', 'js', 'ts', 'jsx', 'tsx']
        }]
      });

      if (selected) {
        // 读取文件内容
        const content = await readTextFile(selected as string);
        setFileContent(content);
      }
    } catch (error) {
      setFileContent(`Error: ${error}`);
    }
  }

  return (
    <div className="container">
      <h1>Welcome to Tauri!</h1>
      
      <div className="section">
        <h2>Greet</h2>
        <form
          onSubmit={(e) => {
            e.preventDefault();
            greet();
          }}
        >
          <input
            onChange={(e) => setName(e.currentTarget.value)}
            placeholder="Enter a name..."
          />
          <button type="submit">Greet</button>
        </form>
        {greetMsg && <p className="result">{greetMsg}</p>}
      </div>

      <div className="section">
        <h2>System Info</h2>
        <button onClick={getSystemInfo}>
          Get System Info
        </button>
        {systemInfo && (
          <pre className="result">{systemInfo}</pre>
        )}
      </div>

      {/* 新增:文件读取 */}
      <div className="section">
        <h2>File Reader</h2>
        <button onClick={selectAndReadFile}>
          Select and Read File
        </button>
        {fileContent && (
          <pre className="result file-content">{fileContent}</pre>
        )}
      </div>
    </div>
  );
}

export default App;

3. 添加文件内容样式

src/App.css 中添加:

css
.file-content {
  max-height: 300px;
  overflow-y: auto;
  font-size: 12px;
  line-height: 1.5;
}

构建生产版本

开发完成后,让我们构建生产版本:

bash
npm run tauri build

构建过程

bash
1. 🔨 构建前端
   - 运行 npm run build
   - 生成优化后的静态文件
   
2. 🦀 构建 Rust 后端
   - Release 模式编译(启用所有优化)
   - 这一步可能需要 5-10 分钟
   
3. 📦 打包应用
   - 生成安装包
   - 位置:src-tauri/target/release/bundle/

输出结果

Windows:
  - .msi 安装包
  - .exe 便携版

macOS:
  - .dmg 安装包
  - .app 应用包

Linux:
  - .deb(Debian/Ubuntu)
  - .rpm(Fedora/RHEL)
  - .AppImage(通用)

体积对比

让我们看看构建结果:

bash
# Tauri 应用
Windows (.msi): ~5-8 MB
macOS (.dmg): ~6-10 MB
Linux (.deb): ~5-8 MB

# 对比 Electron(相同功能)
Windows: ~150 MB
macOS: ~200 MB
Linux: ~120 MB

# 体积差距:约 20-30 倍!

调试技巧

前端调试

bash
# 开发模式会自动打开 DevTools
npm run tauri dev

# 或者在应用窗口中:
- Windows/Linux: F12 Ctrl+Shift+I
- macOS: Cmd+Option+I

后端调试

rust
// 在 Rust 代码中添加日志
#[tauri::command]
fn greet(name: &str) -> String {
    println!("Greet command called with name: {}", name);
    format!("Hello, {}!", name)
}

// 查看日志输出在运行 tauri dev 的终端

常见问题排查

问题 1:命令未定义

bash
错误信息:
command greet not found

解决方案

rust
// 确保命令已注册
fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
            greet,  // ✅ 必须在这里注册
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

问题 2:API 权限被拒绝

bash
错误信息:
api not allowed: fs.readFile

解决方案

json
// 在 tauri.conf.json 中启用 API
{
  "tauri": {
    "allowlist": {
      "fs": {
        "readFile": true  // ✅ 必须明确启用
      }
    }
  }
}

问题 3:热重载不工作

bash
# 检查前端开发服务器
- 确认 Vite 正在运行
- 检查 devPath 配置是否正确

# Rust 代码修改需要重新编译
- 保存 .rs 文件后会自动重新编译
- 如果没有,尝试重启 tauri dev

最佳实践建议

1. 项目组织

推荐的目录结构:
src/
  ├── components/      # React 组件
  ├── hooks/           # 自定义 hooks
  ├── utils/           # 工具函数
  └── tauri-api/       # Tauri API 封装

src-tauri/src/
  ├── commands/        # 命令实现
  ├── utils/           # Rust 工具函数
  └── main.rs          # 主文件

2. 错误处理

rust
// Rust 端
#[tauri::command]
fn my_command() -> Result<String, String> {
    match some_operation() {
        Ok(result) => Ok(result),
        Err(e) => Err(e.to_string())
    }
}

// TypeScript 端
try {
    const result = await invoke('my_command');
} catch (error) {
    console.error('Command failed:', error);
}

3. 类型安全

typescript
// 定义命令参数和返回类型
interface GreetParams {
  name: string;
}

async function greet(name: string): Promise<string> {
  return await invoke<string>('greet', { name });
}

总结

🎉 恭喜你完成了第一个 Tauri 应用!让我们回顾一下学到的内容:

✅ 核心知识点

  1. 项目创建

    • 使用 create-tauri-app 脚手架
    • 选择合适的前端框架
    • 理解项目结构
  2. 前后端通信

    • Rust 端定义命令(#[tauri::command]
    • 前端调用命令(invoke
    • 参数传递和返回值处理
  3. API 使用

    • 文件系统操作
    • 对话框
    • 系统信息
  4. 开发流程

    • 开发模式(tauri dev
    • 热重载
    • 调试技巧
  5. 构建打包

    • 生产构建(tauri build
    • 多平台打包
    • 体积优化

🚀 下一步学习

现在你已经掌握了 Tauri 的基础,可以:

  • 深入学习 Tauri 的核心概念
  • 了解窗口管理
  • 学习更多系统 API
  • 构建实际项目

💡 学习建议

  1. 多动手实践

    • 修改示例代码
    • 尝试不同的 API
    • 解决遇到的问题
  2. 阅读官方文档

    • API 文档很全面
    • 有很多实用示例
    • 社区很活跃
  3. 参考优秀项目

    • GitHub 上搜索 Tauri
    • 学习项目结构
    • 了解最佳实践

下一篇文章,我们将深入探讨 Tauri 的核心概念,包括架构设计、安全模型、配置系统等。


相关文章推荐:

有问题欢迎留言讨论,我会及时解答!


完整代码仓库

参考资源