跳转至

Tauri 2 桌面应用

为什么要学

Tauri 用 Rust + Web 技术构建跨平台桌面应用,是 Electron 的现代替代:

  • 极小体积:应用体积 600KB 起(Electron 动辄 100MB+)
  • 低内存占用:Rust 后端,内存使用仅 Electron 的 1/10
  • 安全第一:Rust 内存安全 + 细粒度权限系统
  • 跨平台:Windows/macOS/Linux,Tauri 2 还支持 iOS/Android
  • 前端自由:任何前端框架(React/Vue/Svelte/Solid)
  • 系统集成:原生菜单、通知、托盘、文件系统等

如果你想构建轻量、安全、高性能的桌面应用,Tauri 是 2024+ 的最佳选择。

核心概念

白话解释

  • 前端:你的 Web 页面(HTML/CSS/JS),显示界面
  • Core(Rust后端):用 Rust 写的系统级功能,处理文件/网络/系统调用
  • IPC:前端和 Rust 后端之间的通信桥梁
  • Webview:操作系统自带的浏览器引擎(不捆绑 Chromium)

核心概念对照表

TauriElectron说明
WebviewChromium渲染前端(使用系统WebView)
Rust CoreNode.js Main后端进程
CommandsIPC handlers前后端通信
PluginsNode modules功能扩展
Permissions(无限制)安全权限控制
~600KB~100MB+应用体积
~20MB RAM~200MB RAM内存占用

安装配置

前置要求

# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 系统依赖
# macOS: Xcode Command Line Tools
xcode-select --install

# Ubuntu
sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget \
  file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev

# Windows: 安装Visual Studio Build Tools + WebView2

创建项目

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

# 选项:
# Project name: my-app
# Frontend: React/Vue/Svelte/Solid/Vanilla
# TypeScript: Yes

cd my-app
npm install
npm run tauri dev

项目结构

my-app/
├── src/              # 前端代码
│   ├── App.tsx
│   └── main.tsx
├── src-tauri/        # Rust后端
│   ├── src/
│   │   ├── main.rs
│   │   └── lib.rs
│   ├── Cargo.toml
│   ├── tauri.conf.json
│   └── capabilities/
│       └── default.json
├── package.json
└── vite.config.ts

快速上手

前后端通信(Commands)

Rust 端定义命令:

// src-tauri/src/lib.rs
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! From Rust!", name)
}

#[tauri::command]
async fn read_file(path: String) -> Result<String, String> {
    std::fs::read_to_string(&path)
        .map_err(|e| e.to_string())
}

pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet, read_file])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

前端调用:

import { invoke } from '@tauri-apps/api/core';

// 调用Rust命令
const greeting = await invoke<string>('greet', { name: '张三' });
console.log(greeting); // "Hello, 张三! From Rust!"

const content = await invoke<string>('read_file', { path: '/tmp/test.txt' });

权限配置

// src-tauri/capabilities/default.json
{
  "identifier": "default",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:allow-read-text-file",
    "fs:allow-write-text-file",
    "dialog:allow-open",
    "dialog:allow-save",
    "notification:default"
  ]
}

文件对话框

import { open, save } from '@tauri-apps/plugin-dialog';
import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs';

// 打开文件
async function openFile() {
  const path = await open({
    filters: [{ name: 'Text', extensions: ['txt', 'md'] }]
  });
  if (path) {
    const content = await readTextFile(path);
    return content;
  }
}

// 保存文件
async function saveFile(content: string) {
  const path = await save({
    filters: [{ name: 'Text', extensions: ['txt'] }]
  });
  if (path) {
    await writeTextFile(path, content);
  }
}

进阶用法

1. 系统托盘

use tauri::{
    menu::{Menu, MenuItem},
    tray::TrayIconBuilder,
};

pub fn run() {
    tauri::Builder::default()
        .setup(|app| {
            let quit = MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;
            let show = MenuItem::with_id(app, "show", "显示窗口", true, None::<&str>)?;
            let menu = Menu::with_items(app, &[&show, &quit])?;

            TrayIconBuilder::new()
                .icon(app.default_window_icon().unwrap().clone())
                .menu(&menu)
                .on_menu_event(|app, event| match event.id.as_ref() {
                    "quit" => app.exit(0),
                    "show" => {
                        if let Some(window) = app.get_webview_window("main") {
                            window.show().unwrap();
                        }
                    }
                    _ => {}
                })
                .build(app)?;
            Ok(())
        })
        .run(tauri::generate_context!())
        .unwrap();
}

2. 插件系统

# 安装官方插件
npm install @tauri-apps/plugin-fs
npm install @tauri-apps/plugin-dialog
npm install @tauri-apps/plugin-notification
npm install @tauri-apps/plugin-shell
npm install @tauri-apps/plugin-store  # 持久化KV存储
// Cargo.toml中添加
[dependencies]
tauri-plugin-fs = "2"
tauri-plugin-dialog = "2"
tauri-plugin-store = "2"

// lib.rs中注册
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_fs::init())
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_plugin_store::Builder::new().build())
        .run(tauri::generate_context!())
        .unwrap();
}

3. 事件系统

// Rust发送事件到前端
#[tauri::command]
async fn start_download(app: tauri::AppHandle) {
    for i in 0..100 {
        app.emit("download-progress", i).unwrap();
        tokio::time::sleep(std::time::Duration::from_millis(50)).await;
    }
}
// 前端监听事件
import { listen } from '@tauri-apps/api/event';

const unlisten = await listen<number>('download-progress', (event) => {
  console.log(`Progress: ${event.payload}%`);
});

// 取消监听
unlisten();

4. 多窗口

import { WebviewWindow } from '@tauri-apps/api/webviewWindow';

const newWindow = new WebviewWindow('settings', {
  url: '/settings',
  title: '设置',
  width: 600,
  height: 400,
  resizable: false,
});

newWindow.once('tauri://created', () => {
  console.log('窗口创建成功');
});

5. 构建发布

# 构建
npm run tauri build

# 输出:
# Windows: .msi / .exe
# macOS: .dmg / .app
# Linux: .deb / .AppImage

# 交叉编译(需要CI)
# 参考: github.com/tauri-apps/tauri-action

常见问题

Q1: 不同平台 WebView 差异?

平台WebView引擎注意事项
WindowsWebView2 (Chromium)需Win10+
macOSWKWebView (WebKit)Safari行为
LinuxWebKitGTK版本可能旧

Q2: 不会 Rust 怎么办?

  • 简单应用可以用 JS 插件完成大部分功能
  • Rust 端只需要写简单的 Command
  • 社区插件覆盖了常见需求
  • Tauri 的 Rust 代码通常很简单

Q3: 和 Electron 如何选择?

方面TauriElectron
体积极小(~1-5MB)大(~100MB+)
性能
生态增长中非常成熟
WebView一致性平台有差异完全一致(Chromium)
学习成本需学少量Rust纯JS

参考资源