客户端
创建tauri应用
yarn create tauri-app
Tauri官网提供了多种创建项目的方式,其他方式有npm,cargo等
创建项目时,前端选择React + Vite.
初始化桌面端的Menu
Windows、MacOs和Linux的Menu不一样,有的Menu在别的系统中无法使用,所以需要自己指定每个系统的Menu。
fn init_menus() -> Menu {
let submenu_gear = Submenu::new(
"Gear",
Menu::new()
.add_native_item(MenuItem::Copy)
.add_native_item(MenuItem::Paste)
.add_native_item(MenuItem::Separator)
.add_native_item(MenuItem::Zoom)
.add_native_item(MenuItem::Separator)
.add_native_item(MenuItem::Hide)
.add_native_item(MenuItem::CloseWindow)
.add_native_item(MenuItem::Quit),
);
let quit = CustomMenuItem::new("quit".to_string(), "Quit");
#[cfg(target_os = "macos")]
let close =
CustomMenuItem::new("close".to_string(), "Close").native_image(NativeImage::UserGuest);
#[cfg(target_os = "macos")]
let submenu_customer = Submenu::new("Customer", Menu::new().add_item(close).add_item(quit));
#[cfg(target_os = "windows")]
let submenu_customer = Submenu::new("Customer", Menu::new().add_item(quit));
let menus = Menu::new()
.add_submenu(submenu_gear)
.add_submenu(submenu_customer);
menus
}
React前端调用后端Rust
要创建一个命令,只需添加一个函数,并使用#[tauri::command]注释:
#[tauri::command]
fn my_custom_command(invoke_message: String) {
println!("I was invoked from JS, with this message: {}", invoke_message);
}
在Rust的main函数中指定该函数:
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![my_custom_command])
.run(tauri::generate_context!())
.expect("failed to run app");
}
在React前端就可以调用这个函数
import { invoke } from '@tauri-apps/api/tauri'
invoke('my_custom_command', { invokeMessage: 'Hello!' })
invoke 函数返回一个用返回值解析的 promise
如果Rust的函数返回了一个错误,在Promise的Response中可以拿到错误信息。
invoke('my_custom_command').then((message) => console.log(message))
Rust主动发送事件给React前端
创建State结构体,内部保存一个AppHandle<Wry>,在tauri创建的时候,将app_handle保存到结构体中,同时由tauri管理。
///创建一个状态管理State 用于发送消息事件
#[derive(Debug,Clone)]
pub struct MessageState {
pub handle: AppHandle<Wry>,
}
impl MessageState {
pub fn new(handle: AppHandle<Wry>) -> Self {
Self { handle }
}
}
tauri::Builder::default()
.setup( |app| {
let callback_app_handle = app.app_handle();
let message_state = MessageState::new(callback_app_handle);
app.manage(message_state);
Ok(())
})
.menu(menu)
.on_menu_event(|event| match event.menu_item_id() {
"quit" => std::process::exit(0),
"close" => {
event.window().close().unwrap();
}
_ => {}
})
.manage(message_sender)
.invoke_handler(tauri::generate_handler![
get_message
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
主动发起事件调用:
React端:
listen('greet', (event: any) => {
// event.payload 才是实际的结构体
console.log(event.payload);
}).then();
Rust端:
在要发送事件函数参数中添加 MessageState
#[tauri::command]
pub async fn index_receive_message(message_sender:State<'_,MessageState>) -> Result<(),()>{
message_state.handle.emit_all("greet","12345").unwrap();
Ok(())
}
集成tonic客户端
添加依赖:
cargo add tonic
toml:
tonic = "0.8"
编写proto文件
syntax = "proto3";
package test;
message LoginRequest {
//手机号码
string phone = 1;
//验证码
string capt = 2;
}
message Token {
string token = 1;
string refreshToken = 2;
}
service ChatService {
//登录
rpc Login(LoginRequest) returns (Token) {}
}
编写build.rs 将proto文件转为Rust代码
///指定生成的位置为pb 使用type_attribute为struct添加额外的derive
fn main() {
tauri_build::build();
tonic_build::configure()
.out_dir("src/pb")
.type_attribute(".","#[derive(serde::Serialize)]")
.compile(&["./chat.proto"], &["./"])
.unwrap();
}