tauri学习(3)-前端调用Rust代码

2022-08-23 16:05:31 浏览数 (1)

接上节继续,今天研究tauri中,前端如何调用Rust代码。

一、无返回值&无传参

main.rs中加1个hello方法:

然后在main方法中,参考下图暴露hello1:

Rust代码准备好之后,前端(假设是react框架)就能调用了:

代码语言:javascript复制
import { invoke } from "@tauri-apps/api/tauri"

先引入invoke方法,然后在需要的地方:

运行效果:

二、有传参

代码语言:javascript复制
/**
 * 可传参
 */
#[tauri::command]
fn hello2(msg: String) {
  println!("hello-2 {}!", msg);
}

多个方法暴露,参考下图:

前端调用:

代码语言:javascript复制
<button onClick={() => invoke('hello2', { msg: "jimmy" })}>hello2</button>

三、有传参&有返回值

代码语言:javascript复制
/**
 * 有传参,带返回值
 */
#[tauri::command]
fn hello3(msg: String) -> String {
  format!("hello-3 {}", msg)
}

前端调用:

代码语言:javascript复制
  let hello3 = (message: String) => {
    invoke("hello3", { msg: message }).then((message) => console.log(message))
  }

四、返回复杂对象

代码语言:javascript复制
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
struct Person {
  name: String,
  age: i32,
}

/**
 * 返回复杂对象
 */
#[tauri::command]
fn get_person(name: String, age: i32) -> Result<Person, String> {
  Ok(Person { name, age })
}

前端调用:

代码语言:javascript复制
let get_person = (name: String, age: Number) => {
  invoke("get_person", { name, age }).then((person) => console.log(person))
}

五、性能测试

很多功能,既然原生js与rust都能实现,谁的性能更高?

还是用经典的fibonacci做为示例:

代码语言:javascript复制
/**
 * 测试性能
 */
#[tauri::command]
fn fibonacci(n: i32) -> i32 {
  if n <= 1 {
    1
  } else {
    fibonacci(n - 1)   fibonacci(n - 2)
  }
}

前端:

代码语言:javascript复制
// js原生版的fibonacci (by:菩提树下的杨过 http://yjmyzz.cnblogs.com)
function fibonacci_js(n: number): number {
  if (n <= 1) {
    return 1;
  }
  return fibonacci_js(n - 2)   fibonacci_js(n - 1);
}

function App() {

  //js版fibonacci测试
  let js_test = (n: number) => {
    let begin = new Date().getTime();
    let result = fibonacci_js(n);
    let end = new Date().getTime();
    console.log(`fibonacci_js(${n})t= ${result},t执行时间: ${end - begin} ms`);
  }


  //rust版fibonacci测试
  let tauri_test = (n: number) => {
    let begin = new Date().getTime();
    invoke('fibonacci', { n }).then((result) => {
      let end = new Date().getTime();
      console.log(`fibonacci_tauri(${n})t= ${result},t执行时间: ${end - begin} ms`);
    });
  }
  
  ...

          <button onClick={() => js_test(38)}>fibonacci_js</button>
          <button onClick={() => tauri_test(38)}>fibonacci_tauri</button>
  ...

}

从输出耗时看,同样的硬件条件情况下,rust的实现,性能高于原生js,但略逊于wasm版本(可参见react rust webAssembly(wasm)示例 )

六、异常处理

Rust代码:

代码语言:javascript复制
/**
 * 异常处理
 */
#[tauri::command]
fn is_valid_age(age: i32) -> Result<String, String> {
  if age > 0 && age < 150 {
    Ok("pass".into())
  } else {
    Err(format!("age:{} invalid", age))
  }
}

前端调用:

代码语言:javascript复制
  let is_valid_age = (age: Number) => {
    invoke("is_valid_age", { age })
      .then((msg) => console.log(msg))
      .catch((err) => console.error(err))
  }

七、Rust异步处理

代码语言:javascript复制
/**
 * 异步方法
 */
#[tauri::command]
async fn method_1() -> String {
  println!("method_1 is called");
  //内部再调用另1个异步方法
  let result = method_2();
  //这里不会block,会继续执行下一句
  println!("do another thing in method_1");
  //这里会阻塞,直到method_2返回
  let result = result.await;
  println!("method_2 result:{} from method_1", result);
  //返回method_2的结果 
  result
}

async fn method_2() -> String {
  println!("method_2 is called");
  //刻意停3秒【注:必须先use std::{thread, time};】
  thread::sleep(time::Duration::from_secs(3));
  format!("method_2 result")
}

前端调用时,并无特别之处,仍按promise的套路处理:

代码语言:javascript复制
  let async_test = () => {
    invoke("method_1").then((result) => {
      console.log("result:", result
      );
    })
  }

Rust终端输出结果:

代码语言:javascript复制
method_1 is called
do another thing in method_1
method_2 is called
method_2 result:method_2 result from method_1

八、访问tauri应用的window对象

代码语言:javascript复制
#[tauri::command]
async fn get_window_label(window: tauri::Window) {
  println!(
    "Window: {},is_fullscreen:{:?}",
    window.label(), //获取应用窗口的label
    window.is_fullscreen() //获取应用是否全屏
  );
}

九、state管理

注:这里的state可不是react组件的state,而是指tauri应用内部的状态,比如应用查询数据库前,要先判断下db的连接状态是否正常,就可以用上这个

代码语言:javascript复制
struct DatabaseState {
  connnted: bool,
}

fn connect_db() -> DatabaseState {
  DatabaseState { connnted: true }
}

#[tauri::command]
fn query_data(state: tauri::State<DatabaseState>) {
  assert_eq!(state.connnted, true);
  println!("query data success")
}

这里我们定义了一个DatabaseState,然后在connect_db方法里,模拟连接上db后,将connected状态置成true,然后在query_data方法中,先判断db是否连上了。

在main方法中,就可以管理状态:

参考文章:

https://tauri.app/v1/guides/features/command

0 人点赞