1
FT-ZTVhNTA0M2U2YTI0M2MyZjI1MDJiMjZiNGYxZjQwYjc6MjYzMjE0MTE4OTY3NQ==.Q8oLvkutpxer0JdRBzDP64hZxb7Z4ozNy3zjM4X0nGArqqKngR/vsYIODbhy09iSciabXnY4zTXq4/95tD/4bS3AV9WoF3uW4LDjI/RLk4AX/fIp7qvPZRTZvN5FHtGZjwP6mgkpeXM0glXra8iI84yw24KVyGaV52jUFZsIa+7OzRdDAzsngWNNEmd1Y4h+jP4uIlUi3kdmnPE6h3n9WJtesbEuKAamd45CWWbrSyieOTSlff8VSWc+Gt9xim1cqvtIcKNP6t0rm8QAdwnDqklnIvwSNsRWju7CbweioQyBkPSeqMeBPQgBs6ajOu0gZa9SsP4U62Hd4NaLHL5YFA==

使用 Tonic 构建 gRPC 服务:从入门到实践

什么是 Tonic?

Tonic 是一个基于 Rust 的高性能 gRPC 实现,它提供了构建 gRPC 客户端和服务器的全套工具。Tonic 使用 HTTP/2 作为传输协议,并支持异步/等待模式,非常适合构建高效的分布式系统。

为什么选择 Tonic?

  • 高性能:基于 Tokio 异步运行时和 Hyper HTTP/2 实现
  • 类型安全:Rust 的强类型系统保证接口安全
  • 易用性:简洁的 API 和良好的开发者体验
  • 生态系统:与 Rust 生态无缝集成

快速开始

1. 创建项目并添加依赖

首先创建一个新的 Rust 项目,然后在 Cargo.toml 中添加以下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[package]
name = "helloworld-tonic"
version = "0.1.0"
edition = "2024"

[[bin]] # 用于运行 gRPC 服务端
name = "helloworld-server"
path = "src/server.rs"

[[bin]] # 用于运行 gRPC 客户端
name = "helloworld-client"
path = "src/client.rs"

[dependencies]
tonic = "*"
prost = "0.13"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }

[build-dependencies]
tonic-build = "*"

2. 定义 Protocol Buffers 接口

在项目根目录下创建 proto/helloworld.proto 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
syntax = "proto3";
package helloworld;

service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
string name = 1;
}

message HelloReply {
string message = 1;
}

3. 构建 Protocol Buffers 代码

创建 build.rs 文件来自动生成 Rust 代码:

1
2
3
4
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/helloworld.proto")?;
Ok(())
}

4. 实现 gRPC 服务端

src/server.rs 中实现服务端逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
use tonic::{transport::Server, Request, Response, Status};

use hello_world::greeter_server::{Greeter, GreeterServer};
use hello_world::{HelloReply, HelloRequest};

pub mod hello_world {
tonic::include_proto!("helloworld");
}

#[derive(Debug, Default)]
pub struct MyGreeter {}

#[tonic::async_trait]
impl Greeter for MyGreeter {
async fn say_hello(
&self,
request: Request<HelloRequest>,
) -> Result<Response<HelloReply>, Status> {
println!("Got a request: {:?}", request);

let reply = HelloReply {
message: format!("Hello {}!", request.into_inner().name),
};

Ok(Response::new(reply))
}
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "127.0.0.1:50051".parse()?;
let greeter = MyGreeter::default();

println!("Server listening on {}", addr);

Server::builder()
.add_service(GreeterServer::new(greeter))
.serve(addr)
.await?;

Ok(())
}

5. 实现 gRPC 客户端

src/client.rs 中创建客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use hello_world::{HelloRequest};
use hello_world::greeter_client::GreeterClient;

pub mod hello_world {
tonic::include_proto!("helloworld");
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = GreeterClient::connect("http://127.0.0.1:50051").await?;

let request = tonic::Request::new(HelloRequest {
name: "Tonic".into(),
});

let response = client.say_hello(request).await?;

println!("RESPONSE={:?}", response);

Ok(())
}

测试服务

1. 启动服务端

1
cargo run --bin helloworld-server

2. 运行客户端测试

1
cargo run --bin helloworld-client

3. 使用 grpcurl 测试

1
2
grpcurl -plaintext -import-path ./proto -proto helloworld.proto \
-d '{"name": "Tonic"}' '127.0.0.1:50051' helloworld.Greeter/SayHello

常见问题解决

1. 版本兼容性问题

确保 tonictonic-buildprost 版本兼容。推荐使用固定版本而非通配符(*)。

2. Protocol Buffers 编译器

确保已安装 protoc 编译器,可以从 Protocol Buffers 发布页 下载。

3. 连接问题

如果遇到连接问题,尝试:

  • 检查服务是否正在运行
  • 确认端口是否正确
  • 尝试使用 127.0.0.1 替代 localhost

总结

Tonic 提供了一个强大而简洁的方式来构建 gRPC 服务。通过本文的指南,你应该能够快速搭建起一个基本的 gRPC 服务,并理解其核心概念。随着对 Tonic 的深入使用,你可以探索更多高级特性,如流式 RPC、拦截器和自定义错误处理等。

Happy coding with Tonic!

Rust Cargo封面图

作为Rust生态的核心工具,Cargo远不止是一个包管理器。本文将系统介绍其核心概念、目录结构和实用技巧,助你高效管理Rust项目。

一、核心概念解析

1. Crate:Rust的模块化单元

  • 定义:Rust中的依赖包或编译单元(官方文档
  • 特点:
    • 可以生成库(lib)或可执行文件(bin)
    • 通过crates.io集中分发

2. Feature:灵活的编译选项

1
2
[dependencies]
serde = { version = "1.0", features = ["derive"] }

📌 关键点

  • 允许选择性启用依赖项的功能
  • 减少最终二进制体积
  • 官方推荐Feature规范

3. 项目类型对比

类型 入口文件 用途 编译命令
lib src/lib.rs 作为库被其他项目引用 cargo build
bin src/main.rs 生成可执行程序 cargo run

二、项目目录结构详解

1
2
3
4
5
6
7
8
.
├── Cargo.lock # 精确锁定的依赖版本([作用说明](https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html))
├── Cargo.toml # 项目元数据和依赖声明
├── examples/ # 示例代码(每个文件都是独立可执行)
├── src/ # 主代码目录
│ ├── lib.rs # 库项目入口
│ └── main.rs # 二进制项目入口
└── tests/ # 集成测试(自动被`cargo test`加载)

💡 测试小技巧

1
2
3
4
5
6
7
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
  • 单元测试:直接写在模块中
  • 集成测试:tests/目录下的独立文件

三、依赖管理实战

1. 三种依赖声明方式

1
2
3
4
[dependencies]
rand = "0.8.5" # crates.io版本
tokio = { git = "https://github.com/tokio-rs/tokio" } # Git仓库
local_lib = { path = "../my-local-lib" } # 本地路径

2. 版本控制语义

符号 示例 含义
^ ^1.2.3 允许1.x.x但不包括2.0.0
~ ~1.2.3 允许1.2.x但不包括1.3.0
* * 任意版本(不推荐生产环境使用)

⚠️ 安全建议:生产项目应使用Cargo.lock固定精确版本

3. 紧急补丁方案

1
2
[patch.crates-io]
some-crate = { git = "https://github.com/user/some-crate", branch = "fix-bug" }

官方文档参考:Overriding Dependencies


四、高级功能:构建脚本

build.rs的典型用途:

  1. 链接系统库
  2. 代码生成
  3. 编译时环境检查

示例(查找OpenSSL):

1
2
3
4
5
// build.rs
fn main() {
println!("cargo:rerun-if-changed=build.rs");
pkg_config::Config::new().probe("openssl").unwrap();
}

更多案例见构建脚本指南


五、国内开发者必备:更换Cargo源

~/.cargo/config中添加:

1
2
3
4
5
[source.crates-io]
replace-with = 'ustc'

[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"

可用镜像源:

  • 中科大:ustc
  • 清华大学:tuna
  • 上海交大:sjtu

延伸阅读

  1. The Cargo Book(官方手册)
  2. Rust模块系统详解
  3. Cargo高级特性

Rust 核心概念解析:宏、借用、Move 和生命周期

目标读者:Rust 初学者
技术栈:Rust(Macro、Ownership、Borrowing、Lifetime)


1. 引言

Rust 以其内存安全和零成本抽象著称,但初学者常常对某些核心概念感到困惑。本文将介绍 宏(Macro)借用与引用(Borrowing & References)Move 语义'static 生命周期,帮助新手快速理解 Rust 的核心机制。


2. 宏(Macro)

是什么?

宏是 Rust 的元编程工具,允许在编译时生成或修改代码,减少重复逻辑。

两种主要宏

  1. 声明宏(macro_rules!
    类似于模式匹配,如 println!
    1
    2
    3
    4
    5
    6
    7
    8
    macro_rules! say_hello {
    () => {
    println!("Hello!");
    };
    }
    fn main() {
    say_hello!(); // 输出: Hello!
    }
  2. 过程宏(Procedural Macros)
    更灵活,可操作 AST(抽象语法树),如 #[derive(Debug)]

适用场景

  • 减少重复代码(如批量实现 trait)。
  • 构建 DSL(领域特定语言)。

官方文档Rust Macros


3. 借用与引用(Borrowing & References)

是什么?

Rust 通过 所有权(Ownership) 管理内存,而 借用(Borrowing) 允许临时访问数据而不获取所有权。

两种引用

  1. 不可变引用(&T
    允许多个读取,但不能修改:
    1
    2
    let s = String::from("hello");
    let len = calculate_length(&s); // 借用 s
  2. 可变引用(&mut T
    唯一且独占,可修改数据:
    1
    2
    let mut s = String::from("hello");
    change(&mut s); // 可变借用

借用规则

  1. 同一时间,要么只能有一个可变引用,要么只能有多个不可变引用。
  2. 引用必须始终有效(无悬垂指针)。

示例验证

1
2
3
4
5
6
7
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 允许
let r2 = &s; // 允许
// let r3 = &mut s; // 错误!已有不可变引用
println!("{}, {}", r1, r2);
}

官方文档Rust Ownership


4. Move 语义

是什么?

Rust 中赋值或传参默认是 移动(Move),所有权转移后,原变量失效。

示例

1
2
3
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权移动到 s2
// println!("{}", s1); // 错误!s1 已失效

例外:实现了 Copy trait 的类型(如 i32bool)会复制而非移动:

1
2
3
let x = 5;
let y = x; // 复制,x 仍然有效
println!("x = {}, y = {}", x, y); // 正确

官方文档Rust Move Semantics


5. 'static 生命周期

是什么?

'static 是 Rust 中最长的生命周期,表示数据在整个程序运行期间有效。

常见场景

  • 字符串字面量(存储在二进制中):
    1
    let s: &'static str = "I live forever!";
  • 全局静态变量
    1
    2
    static VALUE: i32 = 42;
    let r: &'static i32 = &VALUE;

注意:不能随意标记 'static,必须确保数据确实不会释放。

官方文档Rust Lifetimes


6. 总结

概念 核心要点
编译时代码生成,减少重复逻辑。
借用/引用 &T 允许多读,&mut T 允许唯一写,遵守所有权规则。
Move 默认转移所有权,Copy 类型例外。
'static 生命周期标记,表示数据永远有效(如字符串字面量)。

7. 进阶学习

希望这篇博客能帮助你理解 Rust 的核心概念! 🚀

为什么选择Tauri?

在当今多平台应用需求旺盛的时代,前端开发者经常面临这样的需求:如何快速将现有的Web应用打包为桌面程序?传统方案如Electron虽然流行,但存在包体积大、性能开销高等问题。而Tauri作为一个新兴的Rust框架,能生成极小的二进制文件(最小仅几百KB),完美解决了这些问题。

下面我将通过实战演示,只需3步即可完成转换,最后还会以it-tools项目为例展示完整流程。


核心三步曲

第一步:安装Tauri CLI

在项目根目录执行:

1
pnpm add -D @tauri-apps/cli@latest

(也支持npm/yarn安装)

第二步:初始化配置

运行初始化命令并填写基本信息:

1
pnpm tauri init

典型配置示例:

1
2
3
4
5
6
✔ 应用名称? my-app  
✔ 窗口标题? My App
✔ 静态资源路径? ../dist
✔ 开发服务器地址? http://localhost:5173
✔ 开发启动命令? pnpm dev
✔ 构建命令? pnpm build

第三步:启动开发模式

1
pnpm tauri dev

此时会同时启动:

  1. 前端开发服务器
  2. 桌面应用窗口

实战案例:it-tools项目改造

让我们用一个真实项目(it-tools)演示完整流程:

1. 克隆项目

1
2
git clone https://github.com/CorentinTh/it-tools.git
cd it-tools

2. 安装依赖

1
pnpm install

3. 常规启动(验证项目)

1
pnpm dev

4. 集成Tauri

1
2
3
pnpm add -D @tauri-apps/cli@latest
pnpm tauri init
# 按提示填写配置(参考上文)

5. 启动桌面版

1
pnpm tauri dev

启动后可以看应用界面
it-tools


常见问题解答

Q:构建后的应用体积有多大?
A:基础应用约2MB,相比Electron的100MB+有巨大优势。

Q:支持哪些前端框架?
A:所有主流框架(React/Vue/Svelte等)均可使用,Tauri与框架无关。

Q:如何自定义窗口样式?
修改tauri.conf.json中的:

1
2
3
4
5
6
7
8
9
"windows": [
{
"title": "My App",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
]

进阶技巧

  1. 添加系统托盘图标:通过Rust代码实现
  2. 调用原生API:如文件系统操作
  3. 打包发布pnpm tauri build生成各平台安装包

提示:Tauri的Rust后端能力可以逐步探索,初期只需关注前端整合即可。


结语

通过这个教程,我们仅用几条命令就实现了:

  • 保留原有前端开发体验
  • 获得原生桌面应用的所有特性
  • 最终产物极致轻量

如果你正在寻找Electron的替代方案,Tauri绝对是2025年最值得尝试的技术之一。

(完整代码示例可在GitHub获取)
https://github.com/tuyu79/it-tools

引言

系统托盘图标是桌面应用程序中常见的功能,它允许应用在窗口关闭后继续在后台运行,并通过托盘图标提供快捷操作。本文将详细介绍如何为Tauri应用添加这一实用功能。

准备工作

在开始之前,请确保你已经:

  1. 安装了最新版本的Rust和Tauri
  2. 创建了一个基础的Tauri项目

实现步骤

第一步:启用Tauri的托盘功能特性

首先,我们需要在Cargo.toml文件中启用 tray-icon 和 unstable 特性:

1
2
[dependencies]
tauri = { version = "2", features = ["tray-icon","unstable"] }

这个配置会告诉Tauri我们需要使用系统托盘功能。

第二步:编写系统托盘实现代码

以下是完整的实现代码,我们将其放在main.rs或适当的位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
use tauri::{
menu::{Menu, MenuItem},
tray::TrayIconBuilder,
AppHandle, Manager,
};

fn setup_tray(app: &AppHandle) -> tauri::Result<()> {
let main_window = app.get_window("main").unwrap();
let main_window_clone = main_window.clone();

// 监听窗口关闭事件
main_window.on_window_event(move |event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
api.prevent_close();
main_window_clone.hide().unwrap(); // 隐藏窗口而不是关闭
}
});

// 创建托盘菜单项
let show_main = MenuItem::with_id(app, "show_main", "显示主窗口", true, None::<&str>)?;
let quit = MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;
let menu = Menu::with_items(app, &[&show_main, &quit])?;

let app_handle = app.clone();

// 构建并配置托盘图标
TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone())
.menu(&menu)
.show_menu_on_left_click(true) // 左键点击显示菜单
.on_menu_event(move |app: &AppHandle, event| match event.id.as_ref() {
"show_main" => {
app_handle.get_window("main").unwrap().show().unwrap();
}
"quit" => {
app.exit(0);
}
_ => {
println!("未处理的菜单项: {:?}", event.id);
}
})
.build(app)?;

Ok(())
}

代码解析

  1. 窗口关闭处理

    • 我们拦截了窗口的关闭事件,改为隐藏窗口
    • 这确保了应用不会真正退出,而是继续在后台运行
  2. 托盘菜单创建

    • 创建了两个菜单项:”显示主窗口”和”退出”
    • 使用Menu::with_items将它们组合成菜单
  3. 托盘图标配置

    • 使用应用的默认图标作为托盘图标
    • 设置了左键点击显示菜单
    • 为每个菜单项添加了事件处理逻辑

实际应用

要在你的Tauri应用中使用这个功能,只需在应用初始化时调用我们的setup_tray函数:

1
2
3
4
5
6
7
8
9
fn main() {
tauri::Builder::default()
.setup(|app| {
let _ = setup_tray(app.handle());
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

启动后就可以看到效果了

1
pnpm tauri dev

tray icon

注意事项

  1. 跨平台兼容性

    • 不同操作系统对托盘图标的支持可能略有不同
    • 在Linux上可能需要额外的依赖
  2. 图标设计

    • 建议使用清晰简洁的图标
    • 考虑为不同分辨率提供多个尺寸的图标
  3. 用户体验

    • 提供明显的退出选项
    • 考虑添加状态指示功能(如未读消息数)

进阶学习

想要了解更多高级功能,可以参考官方文档:

总结

通过本文的指导,你已经学会了如何为Tauri应用添加系统托盘功能。这个功能可以显著提升你的桌面应用的用户体验,让应用更加专业和实用。


引言

在前端开发中,我们经常需要为桌面应用添加快捷键功能来提升用户体验。本文将介绍如何使用Tauri框架实现通过全局快捷键(如Alt+A)快速创建新窗口的功能。这个功能非常适合需要快速调出辅助窗口的应用场景。

技术栈

  • Tauri - 构建小型、快速、安全的桌面应用框架
  • tauri-plugin-global-shortcut - Tauri全局快捷键插件
  • Vue.js - 前端框架(也可以使用React或其他框架)

基本概念

1. Tauri Webview窗口

Tauri中的WebviewWindow允许我们创建和管理多个浏览器窗口。每个窗口可以加载不同的HTML内容或路由。

2. 全局快捷键

全局快捷键是指在整个操作系统范围内都能响应的快捷键组合,即使应用没有获得焦点也能触发。

3. 进程间通信

Tauri前端(Web)和后端(Rust)通过事件系统进行通信,这使得我们可以在Rust中监听快捷键,然后通知前端创建窗口。

实现步骤

1. 安装必要依赖

首先确保你已经创建了Tauri项目,然后安装全局快捷键插件:

1
pnpm tauri add global-shortcut

依赖安装完后,会自动在 Rust 启动代码里引用插件
add global shortcut plugin

2. 添加创建窗口逻辑

在前端(如Vue组件)中添加窗口创建逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';

const createOrShowShortcutWindow = async () => {
// 检查窗口是否已存在
const existingWindow = await WebviewWindow.getByLabel('shortcut-window');
if (existingWindow) {
await existingWindow.setFocus(); // 存在则聚焦
return;
}

// 创建新窗口
const webview = new WebviewWindow('shortcut-window', {
url: '/', // 加载的路由
center: true, // 居中显示
width: 300, // 宽度
height: 600, // 高度
alwaysOnTop: true // 置顶
});

// 处理窗口创建事件
webview.once('tauri://created', () => webview.show());
webview.once('tauri://error', (e) => {
console.error('窗口创建失败:', e);
});
};

3. 根组件挂载时注册快捷键

1
2
3
4
5
6
7
8
9
10
import {register} from '@tauri-apps/plugin-global-shortcut';

onMounted(async () => {
await register('Alt+A', (event) => {
if (event.state === "Pressed") {
console.log('Shortcut triggered');
createOrShowShortcutWindow()
}
});
});

4. 配置权限

src-tauri/capabilities/desktop.json中添加必要的权限:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"identifier": "desktop-capability",
"platforms": [
"macOS",
"windows",
"linux"
],
"windows": [
"main"
],
"permissions": [
"global-shortcut:default",
"global-shortcut:allow-is-registered",
"global-shortcut:allow-register",
"global-shortcut:allow-unregister",
"core:webview:allow-create-webview",
"core:webview:allow-create-webview-window",
"core:window:allow-show",
"core:window:allow-set-focus",
"core:window:allow-destroy"
]
}

config permission

5. 启动查看效果

1
pnpm tauri dev

按下快捷键 Alt+A, 应该会看到新窗口弹出。
multiple window

示例验证

  1. 运行应用后,按下Alt+A应该会弹出新窗口
  2. 再次按下组合键,如果窗口已存在则会聚焦到该窗口
  3. 可以尝试修改窗口大小、位置等参数查看效果

常见问题

Q: 快捷键没有生效?
A: 检查:

  • 是否正确安装了插件
  • 权限配置是否正确
  • 快捷键是否被其他应用占用

Q: 窗口创建失败?
A: 检查:

  • 新窗口配置的路由是否存在
  • 窗口label是否唯一
  • 控制台错误信息

进阶学习

想要了解更多高级功能,可以参考官方文档:

结语

通过本文,我们学习了如何在Tauri应用中实现全局快捷键创建窗口的功能。这个功能可以大大提升应用的易用性。Tauri的强大之处在于它让前端开发者也能轻松实现原生功能,而无需深入了解系统级编程。

前言

在现代应用开发中,经常需要将高性能的Rust代码与灵活的前端界面结合。Tauri框架为此提供了优雅的解决方案。本文将带你了解如何使用Tauri实现Rust与前端之间的双向通信。

技术栈介绍

我们使用以下技术:

  • Tauri:一个构建小型、快速、安全的桌面应用程序的框架
  • Rust:系统级编程语言,提供高性能和内存安全
  • Vue/React(示例中使用Vue):现代前端框架

基本原理

Tauri提供了一个安全的桥梁,允许:

  1. 前端JavaScript调用Rust函数(命令)
  2. Rust代码触发前端JavaScript事件

这种通信基于进程间通信(IPC)机制,既安全又高效。


第一部分:前端调用Rust函数

第一步:Rust端定义命令

在Rust中,我们需要定义一个可以被前端调用的”命令”:

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

关键点说明

  • #[tauri::command]宏标记这个函数为可从前端调用的命令
  • 函数接收一个String参数,返回一个String
  • 命令名默认与函数名相同(这里是greet

第二步:注册命令

main.rs中注册这个命令:

1
2
3
4
5
6
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

第三步:前端调用命令

在前端代码中(以Vue 3为例):

1
2
3
4
5
6
7
8
9
10
11
import { invoke } from '@tauri-apps/api/core';

async function greet() {
try {
const result = await invoke('greet', { name: 'gulou' });
console.log("收到Rust的回复: ", result);
// 输出: "收到Rust的回复: Hello, gulou!"
} catch (error) {
console.error("调用失败:", error);
}
}

最佳实践

  1. 总是使用try/catch处理可能的错误
  2. 参数需要与Rust函数签名匹配
  3. 使用TypeScript可以获得更好的类型提示

第二部分:Rust调用前端

第一步:前端设置事件监听

在前端组件中(如Vue的onMounted钩子):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { listen } from '@tauri-apps/api/event';

const unlisteners = ref(null);

onMounted(async () => {
try {
const unlisten = await listen('done', (event) => {
console.log("收到Rust的消息: ", event.payload);
});
unlisteners.value = unlisten;
} catch (error) {
console.error("监听失败:", error);
}
});

onUnmounted(() => {
unlisteners.value();
});

第二步:Rust发送事件

在Rust代码中:

1
2
3
4
use tauri::{Emitter};

// 假设app是你的Tauri应用实例
app.emit("done", "hello").unwrap();

高级用法

  • emit发送所有 listener,这里还包括 rust 的 listener
  • emit_to发送给指定的 webview
  • 可以发送任何实现了Serialize的结构体

完整示例:计数器应用

Rust部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use tauri::{Emitter};

#[tauri::command]
fn increment(count: i32) -> i32 {
count + 1
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![increment])
.setup(|app| {
// 启动后2秒发送通知
let app_handle = app.handle().clone();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_secs(2));
app_handle.emit("ready", ()).unwrap();
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

前端部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script setup>
import {ref, onMounted, onUnmounted} from 'vue';
import {invoke} from '@tauri-apps/api/core';
import {listen} from '@tauri-apps/api/event';

const count = ref(0);
const unlisten = ref(null);

onMounted(async () => {
unlisten.value = await listen('ready', () => {
console.log('应用已准备好');
});
});

onUnmounted(() => {
unlisten.value()
})

const increment = async () => {
count.value = await invoke('increment', {count: count.value});
};
</script>

<template>
<button @click="increment">计数: {{ count }}</button>
</template>

最终效果

counter


调试技巧

  1. 前端调试

    • 使用浏览器开发者工具
    • 添加console.log检查通信数据
  2. Rust调试

    • 使用println!宏输出日志
    • 配置RUST_LOG=debug环境变量获取详细日志
  3. 常见问题

    • 确保命令已正确注册
    • 检查事件名称拼写是否一致
    • 验证数据格式是否符合预期

进阶学习

  1. Tauri官方文档(Rust 调用前端)
  2. Tauri官方文档(前端调用 Rust)
  3. 流式数据处理

通过实现前后端的双向通信,你可以构建复杂的Rust-前端交互应用,充分发挥两者的优势!

引言

你是否想过用前端技术开发桌面应用?Tauri 是一个创新的框架,让你能够使用熟悉的 Web 技术(HTML, CSS, JavaScript)构建小巧、快速的跨平台桌面应用。本文将带你从零开始创建你的第一个 Tauri 应用,并解释其中的关键概念。

技术栈介绍

  • Tauri: 一个基于 Rust 的框架,用于构建轻量级桌面应用
  • Rust: 系统级编程语言,提供高性能和安全性
  • Node.js: JavaScript 运行时环境
  • pnpm: 快速、节省磁盘空间的包管理工具

为什么选择 Tauri?

  1. 体积小巧:生成的应用程序比 Electron 应用小很多
  2. 性能优异:基于 Rust 构建,运行效率高
  3. 跨平台:支持 Windows、macOS 和 Linux
  4. 熟悉的开发体验:使用前端技术栈开发
  5. 安全性强:内置安全最佳实践

环境搭建

1. 安装系统依赖

根据你的操作系统,可能需要安装一些基础工具链。请参考 Tauri 官方文档 获取详细要求。

2. 安装 Rust

Rust 是 Tauri 的核心依赖。打开终端运行以下命令:

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

安装完成后,运行以下命令确保 Rust 安装正确:

1
rustc --version

3. 安装 Node.js

推荐使用 nvm 管理 Node.js 版本:

1
2
3
4
5
# 安装 nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash

# 安装最新 LTS 版本的 Node.js
nvm install --lts

或者直接从 Node.js 官网 下载安装包。

4. 安装 pnpm

1
npm install -g pnpm 

创建你的第一个 Tauri 应用

1. 初始化项目

使用 pnpm 创建新项目(也可以使用 npm 或 yarn):

1
pnpm create tauri-app

按照提示操作:

  • 输入项目名称(如 my-tauri-app
  • 选择包管理器(pnpm/npm/yarn)
  • 选择前端框架(Vanilla/React/Vue/Svelte 等)或纯 HTML/CSS/JS

✔ Project name · tauri-app
✔ Identifier · com.tauri-app.app
✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm, deno, bun)
✔ Choose your package manager · pnpm
✔ Choose your UI template · Vue - (https://vuejs.org/)
✔ Choose your UI flavor · TypeScript

2. 安装依赖

进入项目目录并安装依赖:

1
2
cd tauri-app
pnpm install

3. 启动开发服务器

运行开发模式:

1
pnpm tauri dev

这将启动两个进程:

  1. 前端开发服务器(通常是 Vite)
  2. Tauri 应用窗口

你会看到一个桌面窗口,里面渲染着你的前端应用!
Tauri Demo

项目结构简介

1
2
3
4
5
6
7
8
my-tauri-app/
├── src/ # 前端源码
├── src-tauri/ # Tauri 相关配置和 Rust 代码
│ ├── Cargo.toml # Rust 项目配置
│ ├── tauri.conf.json # Tauri 应用配置
│ └── src/ # Rust 源码
├── index.html # 主 HTML 文件
└── package.json # 前端项目配置

打包应用

当你的应用准备好发布时,运行:

1
pnpm tauri build

这会在 src-tauri/target/release 目录下生成可执行文件:

  • Windows: .exe 文件
  • macOS: .dmg 文件
  • Linux: .AppImage.deb 文件

Mac Build Image

如果要修改打包选项,可以编辑 src-tauri/tauri.conf.json 后重新打包
Tauri Config

进阶学习

总结

通过本文,你已经学会了:

  1. Tauri 的基本概念和优势
  2. 如何搭建开发环境
  3. 创建和运行 Tauri 应用
  4. 打包应用

Tauri 为前端开发者打开了桌面应用开发的大门,结合了 Web 开发的便利性和原生应用的性能。现在就开始你的 Tauri 开发之旅吧!

技术文档整理


Gradle 配置优先级(从高到低)

来源 示例 覆盖关系
命令行参数 -PmyProp=value 最高优先级
项目级 gradle.properties ./gradle.properties 覆盖用户级配置
用户级 gradle.properties ~/.gradle/gradle.properties 覆盖环境变量
环境变量 ORG_GRADLE_PROJECT_myProp=value 覆盖脚本默认值
settings.gradle gradle.ext.sharedVersion 影响多项目构建
build.gradle ext { myProp = ... } 最低优先级

Gradle 官方文档


Gradle 依赖缓存位置

1
~/.gradle/caches/modules-2/files-2.1/

Maven 配置

1. 配置文件路径

  • 全局配置${maven.home}/conf/settings.xml
  • 用户配置${user.home}/.m2/settings.xml

    ⚠️ 用户配置优先级更高(合并时覆盖相同配置)。

2. 仓库类型

类型 路径/协议 说明
本地仓库 ${user.home}/.m2/repository 缓存下载的依赖或本地安装
远程仓库 file://https:// 协议 从远程服务器获取依赖

3. 常用插件

  • maven-compiler-plugin:编译 Java 文件(官方提供)。
  • kotlin-maven-plugin:编译 Kotlin 文件(Kotlin 官方提供)。
  • spring-boot-maven-plugin:打包 Spring Boot 为可执行 JAR。

PNPM 全局安装配置

1. 设置全局路径

1
2
pnpm config set store-dir ~/.pnpm-store
pnpm config set global-bin-dir ~/.pnpm-global
  • ~/.pnpm-global 添加到 PATH
    1
    export PATH="$HOME/.pnpm-global:$PATH"

    Windows: $env:PATH += ";$env:USERPROFILE\.pnpm-global"

2. 配置环境变量

  • Linux/macOS(添加到 ~/.bashrc~/.zshrc):
    1
    2
    export PNPM_HOME="$HOME/.pnpm-global"
    export PATH="$PNPM_HOME:$PATH"
  • Windows:通过系统设置添加 PNPM_HOME 变量。

3. 验证配置

1
2
pnpm config list  # 检查路径是否正确
pnpm add -g your-package-name # 重新安装全局包

Shell 配置文件加载时机

文件 加载场景 典型用途
~/.bash_profile 仅登录 Shell(如 SSH、su - 设置环境变量(PATHJAVA_HOME
~/.zshrc 所有交互式 Shell(非脚本运行) 配置别名、主题、插件(如 Oh My Zsh)

0%