这一节,如果你是一个有web开发经验的同学,就非常好理解了,以为Tauri几乎与web开发模式一模一样,稍微有一些不同的只是他们的数据交互和资源调度有一些不同。
下面来我们通过一个简单的例子,来说明Tauri的开发模式:
前后台典型开发模式
先来看一个典型的web开发模式:
先不管前台UI的部分(因为我也不懂,所以我只能跳过),一般来说就是四个主体部分:
1. 前台提交事件
前台一般会把需求提交给后台,例如要获取指定条件的数据,前台会获取空间的值,例如某个时间范围,然后把这个值通过一个from表单和submit事件提交给后台指定的一个url接口,然后等待后台接口响应。
2. 后端路由。
这个部分负责前端调用或者说后台提供给前端的URL与后台实现功能的接口的映射。例如我在后台写了一个鉴权的接口:
async fn authentication(username:&str,
password:&str)->bool{
//代码略
}
那么web前端是无法直接通过这个函数名来调用这个接口的,还需要发布做一个URL与这个接口对应起来,例如在actix-web框架里面,可以通过HttpServer里面的services,把/login这个URL与authentication这方法绑定起来。
HttpServer::new(|| {
App::new()
.wrap(middleware::Logger::default())
.service(web::resource("/login").to(authentication))
}).bind(("0.0.0.0", 8080))?.run()
.await
这一步通常就叫做路由绑定。绑定之后,前端就可以通过http://ip:port/login
这个url来访问这个接口的功能了。
3. 逻辑功能
这部分没啥说的,就是后台写的各种功能逻辑接口,例如需求参数解析、算法实现、数据处理、分析计算统计、数据库CRUD啥的,是后台码农的基本操作,这里就直接略过了。
4. 数据库操作
这部分狭义上可以看成了后台的基本功能,也是后台的代名词,所以后台码农通常被人称为"Curd Boy"的来由。当然,广义上来说,应该是数据源操作,因为后台的数据可能来自redis这种缓存服务器,也可能来自Elasticsearch、HDFS、HBase这种非结构化数据库,甚至可能直接来自NAS、本地磁盘、对象云存储等设备上的各种奇形怪状的文件(比如GIS的可能就直接是读取shapefile或者gdb、geopackage、tiff、三维体元、netcdf等)。
读取完这些数据之后,大部分情况下不能通过二进制推送给前端,一般都会进行一个数据结构的build,例如Restful接口都建议编译成JSON,而图形、视频等非结构化的数据,就转换为流提供给前端。
所以我们在开发的时候,也都通常是分成这四个部分就行开发,除了全栈码农,一般的开发小组都是分成前台和后台两部分,前端负责UI和用户交互部分,后端负责功能实现,前后端之间仅进行数据的交互。
在Tauri里面,也基本上遵循了这个开发模式,下面来我们来简单开发一个用户登录的页面全流程。
Tauri开发示例
1. 前端页面的开发:
我这里写了一个登录用的页面:
(UI白板什么的,就不要在意了)
html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="styles.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri App</title>
<script type="module" src="/main.js" defer></script>
</head>
<body>
<div class="container">
<h1>Tauri 登陆测试</h1>
<form id="vaild-form">
<p>用户名: <input id="vaild-username" placeholder="输入用户名..." /></p>
<p>密码: <input id="vaild-pwd" type="password" placeholder="输入密码..." /></p>
<p><button type="submit">登录</button></p>
</form>
<p id="vaild-msg"></p>
</div>
</body>
</html>
2. 后台路由和逻辑功能的实现:
rust
#[tauri::command]
fn vaild(name: &str,pwd: &str) -> bool{
match name {
"admin" =>{
if pwd == "123"{
true
}
else{
false
}
}
_=>{
if name == pwd {
true
}
else{
false
}
}
}
}
代码的逻辑很简单,如果用户名是admin,则判断密码是不是等于123,如果用户不是admin,则对比用户名密码是不是一样的;通过就返回一个布尔类型的true,验证失败则返回一个布尔类型的false。 (当然,正常代码应该是要通过数据库去获取验证用户名密码的,且密码是要加密的,这里作为测试,我就不搞这么麻烦了,反正逻辑是一样的)。
方法最上面的#[tauri::command]
是Rust语法中的一个属性宏(attribute macro),它用于标记一个函数,使其可以在 Tauri 框架中作为命令(command)使用。
3. 注册路由
rust
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![vaild])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
可以看见,代码中.invoke_handler(tauri::generate_handler![vaild])
这一句,就是把我们的vaild
函数注册成了一个可以在前端调用的方法。
4. 前端事件调用:
注册一个事件,如果触发了前端的submit,则执行下面的方法:
html
window.addEventListener("DOMContentLoaded", () => {
username = document.querySelector("#vaild-username");
pwd = document.querySelector("#vaild-pwd");
vaildMsgEl = document.querySelector("#vaild-msg");
document.querySelector("#vaild-form").addEventListener("submit", (e) => {
e.preventDefault();
vaild();
});
});
调用Rust接口的功能函数编写:
直接用过invoke去调用我们后台编写的vaild方法,传递username和pwd两个参数,然后通过true/false来判断登录是否成功,并且在进行提示:
async function vaild(){
let res = await invoke("vaild", { name: username.value ,pwd: pwd.value });
if (res){
vaildMsgEl.innerHTML = '<font color="green">登录成功</font>';
}
else{
vaildMsgEl.innerHTML = "<font color='red'>登录失败</font>";
}
}
然后运行,测试结果如下: 输入abc/1234,用户名密码不一致,显示登录失败
输入abc/abc,用户密码一致,登录成功
输入admin/123,指定管理员登录成功
打完收工。