项目基本架构
Tauri 是一种使用 Rust 和 Web 技术栈构建跨平台桌面应用的框架
两个主要组成部分:
- 前端部分:Web 技术(HTML、CSS 和 JavaScript),可以使用任何现代的前端框架,如 React、Vue等。前端代码与后端的交互是通过 JavaScript 的 API 与 Rust 后端交互。
- 后端部分(Rust) :Rust 用于实现应用的核心功能和与操作系统的交互。Rust 后端负责创建窗口、访问本地文件系统、处理应用程序逻辑等。Rust 提供了安全、快速和高效的性能,这也是 Tauri 能够做到轻量和高性能的关键。
基础运行环境
基于 tauri 框架, 运行环境按照 tauri 官网的预先准备搭建即可, 基本步骤如下:
- 安装 Microsoft C++ 生成工具
- 安装 WebView2 (window10系统自带)
- 安装 Rust
校验rust安装成功与否
前端到rust端通信链路
运行与开发
版本说明
- tauri-cli: 1.2.0
- Element-plus: 2.9.6
- rustc: 1.85.0
- cargo 1.85.0
启动
- npm install 安装依赖
- 项目根目录执行
tauri dev或者npm run tauri-dev命令启动项目
查看调试日志
- Rust 后端控制台打印日志
# 打印日志
println!("Message from Rust: {}", msg);
# window系统, 让 Rust 编译器打印更多信息
set RUST_BACKTRACE=1 tauri dev
2. WebView 前端控制台
windows系统上快捷键 Ctrl + Shift + i 调出 WebView 控制台, 等价于浏览器的F12控制台
打包
- 构建正式包, 执行命令:
tauri build或npm run tauri-build- 构建产物路径:
src-tauri\target\release
- 构建产物路径:
- 构建调试debug包, 执行命令:
tauri build --debug或npm run tauri-debug- 构建产物路径:
src-tauri\target\debug - debug包可以调出 Rust 后端控制台 和 WebView控制台 方便查看日志输出
- 构建产物路径:
新增命令的PB协议
新命令PB定义
假设PB定义如下:
// dev_update
// 下发
message s254_dev_update_rq {
uint32 dev = 1;
string path = 2;
repeated uint32 id = 3;
uint32 size = 4;
}
// 应答
message s254_dev_update_ra {
uint32 dev = 1;
uint32 check_file = 2;
message dev_update_item {
uint32 id = 1;
uint32 status = 2;
}
repeated dev_update_item status = 3;
}
安装protobufjs-cli
npm install -g protobufjs-cli
编写导入脚本
import fs from 'fs';
import { execSync } from 'child_process';
import { ensureDirSync } from 'fs-extra/esm';
const rq = `message s254_dev_update_rq {
uint32 dev = 1;
string path = 2;
repeated uint32 id = 3;
uint32 size = 4;
}`;
const ra = `message s254_dev_update_ra {
uint32 dev = 1;
uint32 check_file = 2;
message dev_update_item {
uint32 id = 1;
uint32 status = 2;
}
repeated dev_update_item status = 3;
}`;
const rqName = rq.split(' ')[1].split('{')[0].trim();
const raName = ra.split(' ')[1].split('{')[0].trim();
// const cmdName = rqName.split("_")[0];
const nameParts = rqName.split('_');
const cmdName = `${nameParts[0]}${nameParts[1]}`; // d0x30c2104
console.log(rqName, raName, 'rq-ra');
// console.log('name:', rqName, raName, cmdName);
// 使用命令名称创建一个目录
const preDirName = 'protos';
const dirName = `${preDirName}/${cmdName}`;
ensureDirSync(dirName);
const fileName = `${dirName}/${cmdName}.proto`;
const fileContent = `syntax = "proto3";
${rq}
${ra}`;
fs.writeFileSync(fileName, fileContent);
const pathJs = `${dirName}/${cmdName}.js`;
const pathDts = `${dirName}/${cmdName}.d.ts`;
execSync(`pbjs -t static-module -w es6 -o ${pathJs} ${fileName}`);
execSync(`pbts -o ${pathDts} ${pathJs}`);
// console.log(`pbjs -t static-module -w es6 -o ${cmdName}.js ${cmdName}.proto`);
// console.log(`pbts -o ${cmdName}.d.ts ${cmdName}.js`);
console.log(`import { ${rqName}, ${raName} } from "./${preDirName}/${cmdName}/${cmdName}";`);
console.log(`${cmdName}: [${rqName}, ${raName}],`);
执行上面的脚本
node .\src\cmds\protos\tools.js
执行成功后会生成三个协议文件, 如: x.js, x.proto, x.d.ts, 即可引入新生成的协议文件供SendCMDByKeyAsync使用.
tauri v1 迁移至 tauri v2
迁移过程核心变更主要包括两个部分:
- 前端页面以及rust端的各项依赖包的版本更新升级
- 前端页面调整新版本api的等价替代写法
- rust端需要初始化启用各个插件api, 并配置好各个api的使用权限(对应下文的capabilities/default.json)
官方提供了脚手架工具可以一键辅助从v1->v2版本的迁移, 但容易出现问题且不好排查.
个人采用的方法是重新初始化一个最新版本的tauri项目, 再逐步将旧项目文件迁移复用过来, 并逐个配置所使用到的api及其需要的权限.
前端页面变更
- 依赖包版本更新
- api调用写法变更
升级前依赖版本
"@tauri-apps/api": "^1.2.0"
升级后依赖版本, tauri v2版本将许多v1版本中内置的 api 独立为插件, 按需引入更加灵活
"@tauri-apps/api": "^2", // 具体版本: 2.9.2
"@tauri-apps/plugin-dialog": "^2.4.2",
"@tauri-apps/plugin-fs": "^2.4.4",
"@tauri-apps/plugin-opener": "^2.5.2",
"@tauri-apps/plugin-websocket": "^2.0.0",
涉及的部分api写法变更
// 1.2.0
import { invoke } from "@tauri-apps/api/tauri";
import WebSocket from 'tauri-plugin-websocket-api';
import { readBinaryFile } from '@tauri-apps/api/fs';
import { WebviewWindow } from '@tauri-apps/api/window';
import { open } from '@tauri-apps/api/dialog';
const { shell } = await import('@tauri-apps/api'); shell.open(path);
// 2.9.0
import { invoke } from '@tauri-apps/api/core';
import WebSocket from '@tauri-apps/plugin-websocket'
import { readFile } from '@tauri-apps/plugin-fs';
import { getCurrentWindow } from '@tauri-apps/api/window';
const appWindow = getCurrentWindow()
import { open } from '@tauri-apps/plugin-dialog';
const { openPath } = await import('@tauri-apps/plugin-opener')
rust端变更
cargo.toml依赖版本的更新- tauri v2 需要额外在
src-tauri/capabilities/default.json中配置相关api的权限才能正常使用对应的api.
cargo.toml
# 1.2.0
tauri = { version = "1.2", features = ["api-all"] }
# 2.9.2
tauri = { version = "2", features = [] }
tauri-plugin-opener = "2"
tauri-plugin-websocket = "2.0.0"
tauri-plugin-fs = "2.4.4"
tauri-plugin-dialog = "2.4.2"
# 启用插件
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
simple_logger::init_with_level(log::Level::Debug).unwrap();
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_websocket::init())
.manage(SerialManager::default())
.invoke_handler(tauri::generate_handler![
crate::commands::udp_search,
crate::commands::list_ports
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
src-tauri/capabilities/default.json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": [
"main"
],
"permissions": [
"core:default",
"opener:default",
"websocket:default",
"core:window:allow-close",
"dialog:default",
"dialog:allow-save",
"fs:allow-exists",
"fs:allow-app-write",
"fs:allow-app-write-recursive",
"fs:allow-log-write",
"fs:allow-log-write-recursive",
"fs:allow-write-text-file",
"fs:allow-read-text-file",
"fs:allow-read-file",
"fs:allow-log-read",
"fs:allow-log-read-recursive",
"fs:read-files",
{
"identifier": "opener:allow-open-path",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:allow-exists",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "fs:scope",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "core:webview:allow-webview-show",
"allow": [
{
"path": "**"
}
]
},
{
"identifier": "core:webview:allow-create-webview-window",
"allow": [
{
"path": "**"
}
]
}
]
}