记 tauri v1/v2 开发PC调试工具

188 阅读5分钟

项目基本架构

Tauri 是一种使用 Rust 和 Web 技术栈构建跨平台桌面应用的框架

两个主要组成部分:

  1. 前端部分:Web 技术(HTML、CSS 和 JavaScript),可以使用任何现代的前端框架,如 React、Vue等。前端代码与后端的交互是通过 JavaScript 的 API 与 Rust 后端交互。
  2. 后端部分(Rust) :Rust 用于实现应用的核心功能和与操作系统的交互。Rust 后端负责创建窗口、访问本地文件系统、处理应用程序逻辑等。Rust 提供了安全、快速和高效的性能,这也是 Tauri 能够做到轻量和高性能的关键。

基础运行环境

基于 tauri 框架, 运行环境按照 tauri 官网的预先准备搭建即可, 基本步骤如下:

  1. 安装 Microsoft C++ 生成工具
  2. 安装 WebView2 (window10系统自带)
  3. 安装 Rust

校验rust安装成功与否 image.png

前端到rust端通信链路

image.png

运行与开发

版本说明

  • tauri-cli: 1.2.0
  • Element-plus: 2.9.6
  • rustc: 1.85.0
    • cargo 1.85.0

启动

  1. npm install 安装依赖
  2. 项目根目录执行 tauri dev 或者 npm run tauri-dev 命令启动项目

查看调试日志

  1. Rust 后端控制台打印日志
# 打印日志
println!("Message from Rust: {}", msg);

# window系统, 让 Rust 编译器打印更多信息 
set RUST_BACKTRACE=1 tauri dev

2. WebView 前端控制台

windows系统上快捷键 Ctrl + Shift + i 调出 WebView 控制台, 等价于浏览器的F12控制台

打包

  • 构建正式包, 执行命令: tauri buildnpm run tauri-build
    • 构建产物路径: src-tauri\target\release
  • 构建调试debug包, 执行命令: tauri build --debugnpm 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

迁移过程核心变更主要包括两个部分:

  1. 前端页面以及rust端的各项依赖包的版本更新升级
  2. 前端页面调整新版本api的等价替代写法
  3. 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": "**"
        }
      ]
    }
  ]
}

相关文档