目录

📝 文章摘要

一、背景介绍

1.1 Electron 的困境

1.2 Tauri:Rust 的解决方案

二、原理详解

2.1 Tauri 架构

2.2 核心通信:Commands

2.3 核心通信:Events

三、代码实战:系统监控 TUI

3.1 步骤 1:初始化 Tauri + React

3.2 步骤 2:配置配置 tauri.conf.json

3.3 步骤 3:实现 Rust 后端Commands & Events)

3.4 步骤 4:实现 React 前端 (UI)

四、结果分析

4.1 编译与打包

4.2 安全性分析

五、总结与讨论

5.1 核心要点

5.2 讨论问题

参考链接


📝 文章摘要

Tauri 是一个使用 Rust 构建跨平台桌面应用的新一代框架。它巧妙地结合了 Rust 的高性能后端和eb 前端技术(HTML/CSS/JS),替代了 Electron 的 Node.js 运行时,提供了体积更小、内存占用更低、安全性更高的桌面应用。本文将深入探讨 Tauri v2 的架构、其 Rust 后端与 JavaScript 前端的通信机制(Events、Commands),以及如何打包跨平台(Windows, macOS, Linux)的单一二进制应用。我们将实战构建一个系统监控工具,展示 Rust 如何赋能现代桌面开发。


一、背景介绍

1.1 Electron 的困境

Electron(如 VS Code, Slack)通过捆绑 Node.js 和 Chromium 实现了跨平台 GUI。但其代价高昂:

  • 内存占用大:每个应用都包含一个完整的浏览器实例,内存动辄 300MB+。
  • 体积庞大:应用打包后体积巨大(>100MB)。
  • **性能瓶*:Node.js 后端存在 GC 和单线程限制。

1.2 Tauri:Rust 的解决方案

Tauri v2 采用了一种更轻量级的混合架构:

  • Rust 核心:负责所有原生逻辑、系统 API 调用、文件系统和计算。

  • 系统 WebView:使用操作系统内置的 Web 渲染引擎(Windows 的 WebView2, macOS 的 WKWebView, Linux 的 WebKitGTK)。

  • JS/TS 前端:使用 React, Vue, Svelte 等任意前端框架构建 UI。

Tauri 优势:极小的二进制体积、极低的内存占用、Rust 的安全与高性能。


二、原理详解

2.1 Tauri 架构

Tauri 的核心是一个 Rust 进程,它创建并管理一个或多个 WebView 窗口。

在这里插入图片描述

2.2 核心通信:Commands

Commands 是从 JavaScript 调用 Rust 函数的机制。

1. Rust (后端) 定义 Command:

使用 #[tauri::command] 宏,它会自动生成将 Rust 函数暴露给前端的 IPC 绑定。

// src-tauri/src/main.rs

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

**2JavaScript (前端) 调用 Command:**

Tauri 的 JS API (@tauri-apps/api) 提供了 invoke 函数。

// src/main.js
import { invoke } from '@tauri-apps/api/tauri';

async function Greet() {
  // 调用 Rust 后端的 "greet" command
  const response = await invoke('greet', { name: 'World' });
  console.log(response); // "Hello, World! You've been greeted from Rust!"
}

2.3 核心通信:Events

Events 允许 Rust 后端主动向 JavaScript 前端推送数据,非常适合实时更新。

1. Rust (后端) 发送 Event:

// src-tauri/src/main.rs
use tauri::{Window, Manager};

fn emit_system_stats(window: Window) {
    std::thread::spawn(move || {
        loop {
            // 模拟获取 CPU 使用率
            let cpu_usage = (rand::random::::<f64>() * 100.0).floor();
            
            // 向所有窗口广播 "system-stats" 事件
            window.emit("ystem-stats", cpu_usage).unwrap();
            std::thread::sleep(std::time::Duration::from_secs(1));        }
    });
}

2. JavaScript (前端) 监听 Event:

// src/main.js
importrt { listen } from '@tauri-apps/api/event';

async function setupListener() {
  // 监听 "system-stats" 事件
  const unlisten = await listen('system-stats', (event) => {
    console.log('CPU Usage:', event.payload);
    // 更新 UIe.g., setCpuUsage(event.payload))
  });
  
  // 可以在组件卸载时调用 unlisten() 来停止监听
}

三、代码实战:系统监控 TUI

我们将构建一个简单的跨平台应用,实时显示 CPU 和内存使用率。

3.1 步骤 1:初始化 Tauri + React

# 1. 安装 Tauri CLI
cargo install tauri-cli

# 2. 使用模板创建项目 (我们选择 React + TS)
cargo create-tauri-app
# ? App name: system-monitor
# ? Frontend: React (Vite)
# ? UI Template: TypeScript

cd system-monitor
npm install # 安装前端依赖

项目结构

system-monitor/
├── src/                  # React 前端代码
│   ├── main.tsx
│   └── App.tsx
├── src-tauri/            # Rust 后端代码
│   ├── Cargo.toml
│   ├── tauri.conf.json   # Tauri 配置
│   └── src/
│       └── main.rs

3.2 步骤 2:配置配置 tauri.conf.json

我们需要允许 Rust 访问系统信息(这是一个特权操作)。

src-tauri/taurif.json (部分):

{
  "tauri": {
    "allowlist": {
      "all":false, // 默认关闭所有
      "shell": {
        "all": false,
        "open": true // 允许打开链接
      },
      "event": {
        "emit": true, // 允许 Rust -> JS 事件
        "listen": true // 允许 JS -> Rust 事件
      },
      "fs": {
        "all": false
      },
      "window": {
        "all": true // 允许窗口操作
      }
    },
    // ...
  }
}

注意:Tauri 默认是安全的。的。访问文件系统、执行 shell 命令等都需要在 allowlist 中显式开启。

3.3 步骤 3:实现 Rust 后端Commands & Events)

我们需要 systemstat 库来获取系统信息。

src-tauri/Cargo.toml (添加依赖):

[dependencies]
tauri = { version = "2.0.0-beta", features = [] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
systemstat = "0.22.3" # 获取系统信息

src-tauri/src/main.rs

// Prevents additionalconsole window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use serde::Serialize;
use systemstat::{System, Platform, saturating_sub_bytes};
use tauri::{Manager, Window};
use std::time::Duration;

// 1. 定义 Command:获取一次性系统信息
#[derive(Serialize, Clone)]
struct SystemInfo {
    os_name: String,
    cpu_model: String,
    total_memory_gb: f32,
}

#[tauri::command]
fn get_system_info(sys: tauri::State<System>) -> SystemInfo {
    let mem = sys.memory().unwrap();
    SystemInfo {
        os_name: System::os_name().unwrap_or_default(),
        cpu_model: sys.cpu_load_aggregate().unwrap().core_name,
        total_memory_gb: mem.total.as_GB() as f32,
    }
}

// 2. 定义 Event 负载:实时统计
#[derive(Serialize, Clone)]
struct LiveStats {
    cpu_usage_percent: f32,
    memory_used_gb: f32,
}

// 3. 启动实时统计的后台线程
fn start_stats_emitter(window: Window) {
    let sys = System::new();
    
    std::thread::spawn(move || {
        loop {
            // 获取 CPU
            let cpu_load = sys.cpu_load_aggregate().unwrap().done().unwrap();
            
            // 获取内存
            let mem = sys.memory().unwrap();
            let mem_used = saturating_sub_bytes(mem.total, mem.free);

            let stats = LiveStats {
                cpu_usage_percent: (cpu_load.user + cpu_load..system) * 100.0,
                memory_used_gb: mem_used.as_GB() as f32,            };

            // 发送事件
            window.emit("live-stats", stats).unwrap();
            std::thread::sleep(Duration::from_secs(1));
        }
    });
}

fn main() {
    let sys = System::new(); // 创建系统状态
    
    tauri::Builder::default()
        // 1. 将 System 实例作为“状态”注入,供 Command 使用
        .manage(sys)
        // 2. 注册 Commands
        .invoke_handler(tauri::generate_handler![get_system_info])
        // 3. 在应用启动时,启动后台事件发射器
        .setup(|app| {
            let main_window = app.get_window("main").unwrapap();
            start_stats_emitter(main_window);
            Ok(())
        })
        .run(tauri::generate_context!)
        .expect("error while running tauri application");
}

3.4 步骤 4:实现 React 前端 (UI)

src/App.tsx

import { useState, useEffect } from 'react';
import { invoke } from '@tauri-apps/api/tauri';
import { listen } from '@tauri-apps/api/event';
import './App.css';

// 匹配 Rust 后端的 SystemInfo
interface SystemInfo {
  os_name: string;
  cpu_model: string;
  total_memory_gb: number;
}

// 匹配 Rust 后端的 LiveStats
interface LiveStats {
  cpu_usage_percent: number;
  memory_used_gb: number;
}

function App() {
  const [info, setInfo] = useState<SystemInfo || null>(null);
  const [stats, setStats] = useState<LiveStats | null>(null);

  // 1. 启动时用 Command 获取静态信息
  useEffect(() => {
    invoke<SystemInfo>('get_system_info')
      .then(setInfo)
      .catch(console.error);
  }, []);

  // 2. 启动时监听 Event 获取实时数据
  useEffect(() => {
    let unlisten: () => void;
    
    async function setupListener() {
      unlisten = await listen<LiveStats>('live-stats', (event) => {
        setStats(event.payload);
      });
    }
    
    setupListener();
    
    // 组件组件卸载时停止监听
    return () => {
      if (unlisten) unlisten();
    };
  }, []);

  // 3.染 UI
  return (
    <div className="container">
      <h1>Rust System Monitor (Tauri)</h1>
      
      {info && (
        <div className="card">
          <h2>System Info</h2>
          <p><strong>OS:</strong> {info.os_name}</p>
          <p><strong>CPU:</strong> {info.cpu_model}</p>
          <p><strong>Total Memory:</strong> {info.total_memory_gb.toFixed(2)} GB</p>
        </div>
      )}

      {stats && (
        <div className="card">
          <h2>Live Stats</h2>
          <p><strong>CPU Usage:</strong> {stats.cpu_usage_percent.toFixed(1)} %</p>
          <p><strong>Memory Usage:</strong> {stats.memory_used_gb.toFixed(2)} GB</g>
        </div>
      )}
    </div>
  );
}

export default App;

四、结果分析

4.1 编译与打包

# 1. 开发模式 (实时热重载)
cargo tauri dev

# 2. 生产模式 (构建单一二进制安装包)
cargo tauri build

构建结果 (macOS 示例)

  • src-tauri/target/release/system-onitor (可执行文件): ~4.5 MB
  • src-tauri/target/release/bundle/dmg/system-monitor...dmg (安装包): ~2.8 MB
框架 应用 (Hello World) 内存占用 (Idle)
Electron ~120 MB ~150 MB
Tauri (v2) ~5 MB ~25 MB
graph TD
    A[应用体积对比] --> B(Electron (120 MB));
    A --> C(Tauri (5 MB));
    
    D[内存占用对比] --> E(Electron (150 MB));
    D --> F(Tauri (25 MB));
    
    style C fill:#e8f5e9,stroke:#388e3c
    style F fill:#e8f5e9,stroke:#388e3c

分析:Tauri 应用的体积比 Electron 小了 24 倍,内存占用低了 6 倍。这是因为它复用了操作系统的 WebView,而 Electron 捆绑了整个 Chromium。

4.2 安全性分析

Tauri 的 allowlist 机制(默认全关闭)提供了“了“最小权限原则”。如果 tauri.conf.json 没有开启 fs(文件系统)权限,Rust 后端(即使被攻破)也无法用户文件,从根本上提高了安全性。


五、总结与讨论

5.1 核心要点

    • Tauri 架构:Rust 后端 + 系统 WebView 前端。
  • 低开销:实现了极小的二进制体积和内存占用。 * 通信机制

    • #[tauri::command]:JS 调用 Rust (请求/响应)。
    • `windowemit()/listen()`:Rust 推送数据到 JS (事件驱动)。
  • 安全性allowlist 强制执行最小权限,比 Node.js 后端更安全。

  • 生态:可以使用任何 Web 前端框架(React, Vue, Svelte, Solid)构建 UI。

5.2 讨论问题

  1. Tauri 依赖“系统 WebView”是优点还是缺点?(提示:优点是体积小,缺点是跨平台 UI 可能存在细微差异)。
  2. tauri::command 的参数和返回值是如何通过 serde 自动序列化和反序列化的?
  3. 在 Electron 中,前端和后端都是 JS。在 Tauri 中,前端 (JS) 和后端 (Rust) 的职责应该如何划分?
  4. Tauri v2 引入了移动端(iOS/Android)支持,这对 Rust 的跨平台生态意味着什么?

参考链接

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐