Chapter 02

项目创建与目录结构

用 create-tauri-app 快速启动,理解配置文件与目录职责边界

创建第一个 Tauri 项目

# 使用 npm create(推荐)
npm create tauri-app@latest my-tauri-app
cd my-tauri-app

# 脚手架会询问以下问题:
# ? Choose which language to use for your frontend: TypeScript / JavaScript
# ? Choose your package manager: npm / yarn / pnpm / bun
# ? Choose your UI template: Vanilla / Vue / React / Svelte / Solid / Angular

# 安装依赖
npm install

# 启动开发模式(同时启动前端 dev server + Tauri 窗口)
npm run tauri dev
Tauri 开发服务器工作原理

tauri dev 会:1) 启动前端框架的 dev server(如 Vite 的 localhost:1420);2) 编译 Rust 代码;3) 启动 Tauri 应用窗口,WebView 加载前端 dev server 的页面。修改前端代码会热更新,修改 Rust 代码会重新编译并重启窗口。

项目目录结构

my-tauri-app/
├── src/                       # 前端源代码(你熟悉的 Web 世界)
│   ├── main.ts                # 前端入口
│   ├── App.tsx                # 根组件(React 示例)
│   └── assets/
├── src-tauri/                 # Rust 后端(Tauri 核心)
│   ├── src/
│   │   ├── main.rs            # Rust 入口,创建 Tauri App
│   │   └── lib.rs             # Commands 和插件注册
│   ├── capabilities/          # 权限配置(Tauri 2.0 新增)
│   │   └── default.json       # 默认窗口权限
│   ├── icons/                 # 应用图标(多种尺寸)
│   ├── tauri.conf.json        # Tauri 主配置文件
│   └── Cargo.toml             # Rust 依赖管理
├── public/                    # 静态资源(直接复制到产出)
├── index.html                 # Vite 入口 HTML
├── vite.config.ts             # Vite 配置
├── package.json               # 前端依赖
└── tsconfig.json              # TypeScript 配置

tauri.conf.json 详解

这是 Tauri 的主配置文件,控制应用的方方面面:

{
  "productName": "my-tauri-app",
  "version": "0.1.0",
  "identifier": "com.example.myapp",  // 唯一应用ID(反向域名格式)
  "build": {
    "beforeDevCommand": "npm run dev",   // dev 时先启动前端
    "beforeBuildCommand": "npm run build", // build 时先构建前端
    "devUrl": "http://localhost:1420",    // dev 模式 WebView 加载地址
    "frontendDist": "../dist"            // 生产构建产出目录
  },
  "app": {
    "windows": [
      {
        "label": "main",
        "title": "My Tauri App",
        "width": 1200,
        "height": 800,
        "minWidth": 800,
        "minHeight": 600,
        "resizable": true,
        "fullscreen": false,
        "decorations": true   // false 则无边框窗口
      }
    ],
    "security": {
      "csp": null  // Content Security Policy(生产环境建议配置)
    }
  },
  "bundle": {
    "active": true,
    "targets": "all",           // 或 ["nsis", "dmg", "appimage"]
    "icon": [
      "icons/32x32.png",
      "icons/128x128.png",
      "icons/icon.icns",        // macOS 图标
      "icons/icon.ico"          // Windows 图标
    ]
  }
}

Cargo.toml:Rust 依赖管理

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

[lib]
name = "my_tauri_app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]

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

[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]
custom-protocol
生产模式使用自定义协议(tauri://)加载前端资源,而非 HTTP 服务器。开发模式下禁用,使用 Vite dev server。
crate-type
移动端需要 cdylib(动态库)和 staticlib(静态库),rlib 用于 Rust 单元测试。

Rust 入口文件

// src-tauri/src/main.rs
// 防止 Windows 弹出控制台窗口(生产模式)
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

fn main() {
    // 调用 lib.rs 中定义的 run() 函数
    my_tauri_app_lib::run();
}

// src-tauri/src/lib.rs
use tauri::Manager;

// 注册 Command(第4章详解)
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        // 注册插件
        .plugin(tauri_plugin_fs::init())
        .plugin(tauri_plugin_dialog::init())
        // 注册 Commands
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

开发与构建命令

DEV

npm run tauri dev

启动开发模式。同时运行前端 dev server 和 Tauri 窗口,支持前端热更新(HMR)。修改 Rust 代码会自动重新编译(约需 5-30 秒)。

BUILD

npm run tauri build

生产构建。先运行 npm run build 构建前端,再用 cargo build --release 编译 Rust,最后用打包工具生成安装包(NSIS/dmg/AppImage)。

INFO

npm run tauri info

显示当前环境诊断信息:Tauri 版本、Rust 版本、Node 版本、WebView 版本、操作系统信息等。排查问题时的首选命令。

ICON

npm run tauri icon [path/to/app-icon.png]

从一张 1024x1024 的 PNG 图标自动生成所有平台需要的图标尺寸(32x32、128x128、.icns、.ico 等),输出到 src-tauri/icons/

MOBILE

npm run tauri ios dev / tauri android dev

移动端开发模式。iOS 启动模拟器,Android 启动 AVD 或连接真机。需要预先完成移动端工具链配置。

capabilities/ 权限系统

// src-tauri/capabilities/default.json
{
  "identifier": "default",
  "description": "主窗口默认权限",
  "windows": ["main"],       // 适用于哪些窗口(label 列表)
  "permissions": [
    "core:default",            // Tauri 核心默认权限
    "fs:allow-read-text-file", // 允许读文本文件
    "fs:allow-write-text-file",
    "fs:scope"                 // 配合 scope 限制可访问路径
  ]
}
权限 ID 命名规则

格式为 插件名:allow-动作名插件名:deny-动作名。查看所有可用权限:cargo tauri permission list。也可以在 Tauri 文档的各插件页面中找到权限列表。

配置文件高级选项

{
  "app": {
    "windows": [{
      "label": "main",
      "title": "My App",
      "width": 1200, "height": 800,
      "minWidth": 800, "minHeight": 600,
      "center": true,
      "decorations": true,   // false = 无边框窗口
      "transparent": false,  // 透明背景(需要 transparent feature)
      "visible": true         // false = 系统托盘应用不自动显示
    }]
  },
  "bundle": {
    "active": true,
    "targets": "all",        // 或指定 ["dmg", "msi", "deb"]
    "identifier": "com.mycompany.myapp",
    "icon": [
      "icons/32x32.png",
      "icons/128x128.png",
      "icons/icon.icns",   // macOS
      "icons/icon.ico"     // Windows
    ],
    "updater": {
      "active": true,
      "endpoints": ["https://releases.myapp.com/{{target}}/{{arch}}/{{current_version}}"],
      "pubkey": "..."  // 通过 tauri signer generate 生成
    }
  }
}

Rust 全局状态管理

Tauri 允许在 Rust 侧维护全局状态,通过 manage() 注册,在 Command 中通过 State 参数访问:

use std::sync::Mutex;
use tauri::State;

struct AppState {
    counter: u32,
}

type SharedState = Mutex<AppState>;

#[tauri::command]
fn increment(state: State<'_', SharedState>) -> u32 {
    let mut s = state.lock().unwrap();
    s.counter += 1;
    s.counter
}

#[tauri::command]
fn get_count(state: State<'_', SharedState>) -> u32 {
    state.lock().unwrap().counter
}

fn main() {
    tauri::Builder::default()
        .manage(Mutex::new(AppState { counter: 0 }))
        .invoke_handler(tauri::generate_handler![increment, get_count])
        .run(tauri::generate_context!())
        .unwrap();
}

Vite 配置与 Tauri 集成

vite.config.ts 关键配置

Tauri 项目的 Vite 配置需要特别注意几个关键选项,确保前端代码能正确加载系统资源和调用 Tauri API:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

// 是否在 Tauri 环境中运行
const host = process.env.TAURI_DEV_HOST;

export default defineConfig({
  plugins: [react()],

  // Vite dev server 配置
  server: {
    // Tauri 需要一个固定的主机名(不能用 localhost,移动端需要局域网 IP)
    host: host ?? false,
    port: 1420,
    strictPort: true,  // 端口被占用时报错而非自动换端口
    // HMR 配置(Tauri 移动端开发需要)
    hmr: host
      ? {
          protocol: 'ws',
          host,
          port: 1421,
        }
      : undefined,
  },

  // 清除前端浏览器警告(Tauri API 不是浏览器 API)
  envPrefix: ['VITE_', 'TAURI_'],

  build: {
    // 输出目录(与 tauri.conf.json 的 frontendDist 一致)
    outDir: '../dist',
    // Tauri 使用 Chromium,支持现代特性,不需要降级兼容
    target: process.env.TAURI_ENV_PLATFORM == 'windows'
      ? 'chrome105'
      : 'safari13',
    // 开发构建禁用 minify,加快编译
    minify: !process.env.TAURI_ENV_DEBUG ? 'esbuild' : false,
    // 关闭 sourcemap(减小产出体积)
    sourcemap: !!process.env.TAURI_ENV_DEBUG,
  },
});

环境变量

区分开发和生产环境

Tauri 在不同场景下注入不同的环境变量,可以用来实现环境感知的行为:

// 前端代码中判断运行环境

// 是否在 Tauri 中运行(区别于纯 Web 浏览器)
const isTauri = '__TAURI_INTERNALS__' in window;

// 是否是 debug 构建(tauri dev 或 tauri build --debug)
const isDev = import.meta.env.DEV;
const isProd = import.meta.env.PROD;
const mode = import.meta.env.MODE; // "development" 或 "production"

// 用户自定义 VITE_ 前缀变量
// .env.development:
//   VITE_API_URL=http://localhost:3000
// .env.production:
//   VITE_API_URL=https://api.myapp.com
const apiUrl = import.meta.env.VITE_API_URL;

// Tauri 环境信息(由 Tauri 注入)
// process.env.TAURI_ENV_PLATFORM: "windows" | "linux" | "macos" | "ios" | "android"
// process.env.TAURI_ENV_ARCH:     "x86_64" | "aarch64" | "i686" | "armv7"
// process.env.TAURI_ENV_DEBUG:    "true" | undefined
const platform = import.meta.env.TAURI_ENV_PLATFORM;

// 根据平台显示不同内容
function getPlatformShortcut(action: string): string {
  if (platform === 'macos') {
    return `⌘${action}`;
  }
  return `Ctrl+${action}`;
}
// Rust 端获取环境信息
#[tauri::command]
fn get_app_info() -> serde_json::Value {
    // CARGO_PKG_VERSION 在编译期替换为实际版本号
    let version = env!("CARGO_PKG_VERSION");

    // cfg! 宏:编译期条件(不影响运行时性能)
    let is_debug = cfg!(debug_assertions);
    let platform = if cfg!(target_os = "windows") {
        "windows"
    } else if cfg!(target_os = "macos") {
        "macos"
    } else {
        "linux"
    };

    serde_json::json!({
        "version": version,
        "isDebug": is_debug,
        "platform": platform,
    })
}

开发效率提升

加速 Rust 编译

Tauri 的 Rust 编译(特别是首次)可能需要 2-5 分钟。以下方法可以显著加速:

# src-tauri/.cargo/config.toml(项目级 Cargo 配置)

[build]
# 使用 mold 链接器(Linux,速度是 ld 的 5-10 倍)
# rustflags = ["-C", "link-arg=-fuse-ld=mold"]

# macOS 使用 lld 链接器
# rustflags = ["-C", "link-arg=-fuse-ld=lld"]

[profile.dev]
# 开发模式优化:减少单文件优化,提高并行编译度
opt-level = 0
debug = true

[profile.dev.package."*"]
# 依赖库仍然用高优化级别(只有你自己的代码是 debug)
opt-level = 3

# 可选:cranelift 后端(更快的编译,但不适合生产)
# [unstable]
# codegen-backend = "cranelift"
# 工具链加速选项

# 安装 cargo-watch(自动检测变更并重新编译)
cargo install cargo-watch

# 使用 sccache 缓存编译结果(跨次构建复用)
cargo install sccache
export RUSTC_WRAPPER=sccache

# 查看编译耗时分解
cargo build --timings

# 只检查错误,不生成二进制(最快的语法检查)
cargo check
只重新编译修改的 Crate

Rust 的增量编译已经相当智能——只有你修改的文件所在 Crate 会重新编译。但第一次和清空缓存后(cargo clean)会全量编译,这时候几分钟是正常的。避免在开发时频繁运行 cargo clean。如果修改的是 tauri.conf.json 等配置文件,无需重新编译 Rust,只需要重新运行 tauri dev

本章核心要点