初次体验Tauri和Sycamore(1)

原创作者:庄晓立(LIIGO)
原创时间:2024年11月10日
原创链接:https://blog.csdn.net/liigo/article/details/143666827
版权所有,转载请注明出处。

tauri-splash

前言

Tauri 2.0发布于2024年10月2日,Sycamore 0.9发布于2024年11月1日。二者在近期双双发布重大版本升级,是我(LIIGO)本次想体验他们的主要动机。Tauri自2022年发布v1.0之后就早已火出天际,而Sycamore自2022发布v0.8之后沉寂了两年之久,如今各自凤凰涅槃,他们的组合体会擦出怎样的火花?

关于Tauri

Tarui用于创建小巧、快速、安全、跨平台的桌面GUI应用和移动应用软件。

Create small, fast, secure, cross-platform applications.

Tauri APP主要由三个子系统组成,Webview + Shell + Rust。其中Webview是前端WEB载体,用于展现UI与用户交互;Shell是Webview的载体,提供窗口/菜单/通知等OS级支撑;Rust是后端核心,为前端提供功能支撑和运行时支撑。前端WebUI + 后端Rust业务逻辑,整合了WEB在布局和UI的优势以及Rust后端功能性能优势,是颇具竞争力的跨平台GUI应用开发模式,且已被Electron和Tauri实证有效。在Rust编程领域,主流NativeUI框架稀缺或生态不成熟,主动拥抱生态极其丰富的JS/TS前端框架是明智之举。

为什么需要Rust在后端提供功能支撑呢?因为前端App运行在Webview中,那是沙箱环境,其功能受限(例如不能启动OS进程/执行CLI),因而需要寻求拓展外界支持。对于Electron而言,由Nodejs提供后端支撑;对于Tauri而言,由Rust提供后端支撑。这也是Tauri和Electron的本质区别。区别的本质是不同程序员群体做出的不同倾向性选择:Rust程序员倾向于选择Rust,JS/TS程序员倾向于选择Nodejs。Tauri的核心运行时库也被编译进后端子系统,跟Tauri App的后端业务逻辑代码处于同一Crate。

Tauri 2.0开始引入插件(Plugins)生态,并且预置了一批立等可取的插件,可视为对后端的拓展,缓解了对后端的强依赖,对非Rust开发者更是福音。它的插件往往是跨桌面系统(Windows/Linux/macOS)和移动系统(Android/iOS)提供统一的接口。除了官方插件外,还有第三方插件可供选择。

抛开后端再看Webview,Tauri和Electron还有一个重大区别:Electron App内嵌携带Webview即Chromium;而Tauri App不携带Webview,它直接使用App用户OS里的Webview(Windows内的WebView2,macOS内的WKWebView,Linux内的webkit2gtk)。Electron的优势是Webview已知、确认与App兼容,劣势是App尺寸过大(上百MB)。Tauri的优势是App尺寸很小(十余MB),劣势是Webview未知、与App兼容性未知。此处Tauri的劣势是可控的、可接受的,理由如下:1) 各主流平台的Webview已经逐渐趋同(用现代的或相同的浏览器内核);2) Tauri为App生成的安装程序(Installer)会协助用户获取兼容Webview;3) WEB前端App原本就不应该使用厂商专用特性。4) 做网站也是要面对不同的Webview的,该踩得坑全球网友都帮你已经踩过了。

作为Tauri App的开发者,你没法选Webview(由目标用户的操作系统而定),也没法选后端语言(确定使用Rust语言),但你可以自由的选择前端框架(取决于你个人或团队的习惯和偏爱)。类似于Electron,Tauri也支持多种主流前端框架,如React、Vue、Svelte、Solid、Angular、Preact等等。Tauri不仅支持前述JS/TS前端,还支持WASM前端,如Yew、Leptos、Sycamore、Dioxus等(Rust语言),以及.Net的Blazor前端(C#语言)。当然你也可以不用任何框架(Vanilla,纯JavaScript/CSS/HTML)。

创建App

Tauri使用两个命令行工具 (create-tauri-app, tauri-cli) 创建和编译打包App。首先要安装这两个CLI:

cargo install create-tauri-app --locked
cargo install tauri-cli --version "^2.0.0" --locked

create-tauri-app, tauri-cli 从Rust源码编译(cargo install)都相当耗时(均依赖数百个crates)。我还是建议自行下载编译后版本放到cargo bin目录。我给他们提了建议,今后会推荐使用 cargo binstall 下载编译好的二进制CLI,而不是使用cargo install从源码开始编译。

这两个CLI也有都对应的npm包:create-tauri-app, @tauri-apps/cli,二者都是间接调用Rust编译好的可执行文件。供TS/JS前端使用。

执行如下命令开始创建Tauri app:cargo create-tauri-app。CLI会逐步引导你输入或选择如下信息:

  • 项目名称(Project name),默认是"tauri-app"
  • Identifier 默认是"com.tauri-app.app"
  • 前端语言,可选 Rust, TS/JS, .Net
  • UI模板,视前端语言而定
    • Rust UI模板:可选 Vanilla, Yew, Leptos, Sycamore, Dioxus
    • TS/JS UI模板:可选 Vanilla, Vue, Svelte, React, Solid, Angular, Preact
    • .Net UI模板:可选 Blazor

选TS/JS的UI模板前还需要选择包管理器:pnpm, yarn, npm, deno, bun

因为这次我(Liigo)想体验Tauri+Sycamore,因而前端语言选Rust,UI模板选Sycamore。

目录结构

Tauri+Sycamore App目录结构:

├─ public/
├─ src/
│  ├─ app.rs
│  └─ main.crs
├─ src-tauri/
│  ├─ ...
│  ├─ Cargo.toml
│  └─ tauri.conf.json
├─ .gitignore
├─ .taurignore
├─ Cargo.toml
├─ index.html
├─ README.md
├─ styles.css
└─ Trunk.toml

Tauri源码目录对前端和后端代码进行了隔离。后端代码使用src-tauri/子目录;前端代码使用除此之外的其他文件和子目录。

编译打包

开发版

cargo tauri dev

编译完成后自动启动App,弹出GUI主窗口。允许开发者在App运行过程中修改前端源代码,Tauri(或者说Trunk)会自动编译,并刷新App窗口内容,但是App并不会中途退出或重启(原理:Trunk通过WebSocket向开发版App推送重新加载UI的指令;Dioxus虽然没用Trunk但也实现了类似机制)。

它会检查Trunk是否存在,不存在的话会自动下载源码并编译。但是在Windows下编译Trunk很可能会碰到如下问题(间接依赖openssl开发者库):

  It looks like you're compiling for MSVC but we couldn't detect an OpenSSLinstallation. If there isn't one installed then you can try the rust-opensslREADME for more information about how to download precompiled binaries ofOpenSSL:https://github.com/sfackler/rust-openssl#windowsnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...
error: failed to compile `trunk v0.21.2`, intermediate artifacts can be found at `E:\tmp\RUST_DIR\CARGO_TARGET_DIR`.
To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path.

我找到的解决办法是,去github的trunk官方仓库下载编译好的trunk.exe,丢进cargo bin目录即可(或任意PATH目录均可)。

如果cargo tauri dev过程中看到如下提示时只需耐心等待:

Warn Waiting for your frontend dev server to start on http://localhost:1420/…

这是因为Trunk先启动了(WEB服务监听1420端口),等待APP主动连接。但是编译App需要时间,等它编译完并启动后才能连上。

通过App窗口右键菜单可以打开devtools。在App运行过程中,还可以在浏览器中打开 http://localhost:1420/ ,网页内容跟GUI窗口是一样的(可视为App的另一个实例)。

发行版

cargo tauri build

编译App并打包为安装包。

如果编译过程中提示正在下载Wix但失败(Github国内连接不稳定):

Downloading https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip

你可以通过其他方法手动下载此连接,解压到如下目录:C:\Users\liigo\AppData\Local\tauri\WixTools314\(里面有一堆exe等文件)。

此方法是我(LIIGO)从 Tauri仓库源码 里扒出来的。实证管用。

同理,如果NSIS也下载不了,可以用类似的办法手动下载解压到目录C:\Users\liigo\AppData\Local\tauri\NSIS。但是我没用这个方法。因为我觉得既然已经有Wix用来生成MSI安装包,就没必要再下载NSIS(生成另一种安装包)。

我研究了一下,将配置文件tauri.conf.json里面的bundle.targets改为"msi"(原来是"all")即可禁用NSIS等。

文件大小

Tauri+Sycamore app编译后是一个可独立运行的图形用户界面(GUI)exe,其内部整合了wasm/css/图片等文件,没有其他外部依赖。exe文件大小是10.3MB,对应的安装包msi文件大小是3.6MB(安装后也只有那个exe和一个用于卸载的快捷方式文件(指向系统文件msiexec.exe /x))。sycamore生成的wasm文件大小为750KB(已包含在exe中)。App启动时有大约一两秒的窗口白屏。

作为对比,再看一下Tauri+Dioxus app的数据:exe大小10.6MB,mis大小3.9MB,wasm文件大小为1.3MB(debug版33MB或25MB),也有一两秒的启动白屏。大同小异吧。我(LIIGO)暂且认为这是Tauri App (Hello world)的平均水平。

这样的文件大小应该很香吧。最起码比Electron app香多了。

1MB的wasm文件,用在普通网站上,网络传输加载延迟是一个较大的负担,但是对Tauri app而言这就是本地加载啊,性能没得说。

资源占用

Tauri+Sycamore app启动后,内部加载3到6个Webview2进程,连同exe合计占用内存60到90MB。

无操作时CPU占用率为0%;在app窗口上移动鼠标时,CPU占用率逐步上升到10%甚至更多。这个问题是不是需要改善呀。

命令(Commands)

前端可以调用后端定义的Command。反之则不行,因为只有后端有Command,前端没有。

TS/JS前端调用Command

Tauri给前端提供了调用后端Command的通用接口,invoke函数:

import { invoke } from '@tauri-apps/api/core';
const result = invoke('greet', 'liigo'); // 调用后端greet命令并接收返回值

@tauri-apps/api是Tauri发布到npm的多个package之一,可供Tauri所有TS/JS前端框架使用。

示例greet是在后端Rust代码中自定义的Command(有参数有返回值):

#[tauri::command]
fn greet(name: &str) -> String {format!("Hello, {}! You've been greeted from Rust!", name)
}

注意:新增Command后需要同步更新lib.rs文件内的tauri::generate_handler![greet]调用。

前端调用后端,本质是进程间通讯(IPC),invoke()底层利用的是各Webview的专有实现,例如window.chrome.webview.postMessage()

Rust前端调用Command

但是,我现在用的是Rust前端啊,@tauri-apps/api用不上啊。

没关系,Tauri还通过另一种方式为前端提供接口:window.__TAURI__,大致等效于@tauri-apps/api。此接口存在的前提是事先修改tauri.conf.json文件配置app.withGlobalTauritrue

于是,invoke函数就在这里:window.__TAURI__.core.invoke。Rust前端和TS/JS前端都能使用。

调用实例:

#[wasm_bindgen]
extern "C" {#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"])]async fn invoke(cmd: &str, args: JsValue) -> JsValue;
}#[derive(Serialize, Deserialize)]
struct GreetArgs<'a> {name: &'a str,
}let name = "liigo";
let args = serde_wasm_bindgen::to_value(&GreetArgs { name }).unwrap();
let new_msg = invoke("greet", args).await.as_string().unwrap();

可以看出,从Rust前端调用Rust后端,反而比TS/JS更麻烦。麻烦的点是:

  • 需要借助#[wasm_bindgen]声明invoke函数
  • 需要定义被调用command的参数struct并支持序列化
  • Command的参数值需要先转换为JsValue类型才能传入invoke函数
  • invoke返回值是JsValue类型,还要转换类型才能使用

我想这可能是大伙儿不愿意在Tauri中使用Rust前端的原因之一。不知道后续能不能改善。现阶段还是使用TS/JS前端更有生产力,生态也更成熟。

题外话:Rust前端调用Rust后端?都是Rust,代码直接写在前端是不是就不用调用后端了?你想多了。Rust前端是被编译成WASM、在Webview沙箱环境中执行的,其功能也是受限的,有时候不可避免依赖Rust后端。

事件(Events)

前端可以给后端发送事件,后端也可以给前端发送事件。

前端给后端发送Event

TS/JS前端使用@tauri-apps/api package 内的emit()函数给后端发送Event。

import { emit } from '@tauri-apps/api/event';
await emit('frontend-loaded', { loggedIn: true, token: 'authToken' });

emit()实际上只是对invoke()的简单封装:

async function emit(event, payload) {await invoke('plugin:event|emit', { event, payload });
}

(上述invoke调用中的'plugin:event|emit'似乎暗示event也是一个插件?我研究后发现的确如此。)

Rust前端给后端发送Event,比照前文调用window.__TAURI__.event.emit()window.__TAURI__.core.invoke()

后端给前端发送Event

使用 tauri crate:

  • tauri::AppHandle::emit
  • tauri::App::emit
  • tauri::webview::WebviewWindow::emit
  • tauri::webview::Webview::emit
  • tauri::window::Window::emit

例如:

use tauri::Emitter;#[tauri::command]
fn synchronize(window: tauri::Window) {window.emit("synchronized", ());
}

Rust前端如何获取tauri::Window对象呢?我估计是比照前文调用window.__TAURI__.window.getCurrentWindow()

通道(Channels)

通道用于在前后端之间快速双向传输大块数据、流数据,传输是有序的,先发先到。

通道在前端的JavaScript类型是@tauri-apps/api/core/Channel,位于@tauri-apps/api package内;通道在后端的Rust类型是tauri::ipc::Channel,位于tauri crate内。以上二者是Channel的一体两面,同一个Channel对象,在前端的表现为JS Channel,在后端表现为Rust Channel。

应用示例:https://tauri.app/develop/calling-frontend/#channels

插件(Plugins)

前端和后端都可以调用插件。

Tauri插件本身就是Rust的crate(例如tauri-plugin-dialog),在后端Rust代码中调用插件,跟调用其他crate一样,直接cargo add就OK了。

每个插件都有对应的npm包(例如@tauri-apps/plugin-dialog)提供TS/JS接口(通常是自动化生成),供TS/JS前端调用。如果是Rust前端呢,大概要麻烦一些,参考前端调用Command。

前面用于调用Command的invoke()函数也具备调用插件的功能,调用语法是:invoke('plugin:插件名|函数名', { 参数 })

插件的TS/JS接口实际上也只是对invoke()的简单封装,例如:

async function open(options = {}) {if (typeof options === 'object') {Object.freeze(options);}return await invoke('plugin:dialog|open', { options });
}

总结

近两年来Tauri受到大量关注,其Github仓库STAR已高达84.7K,已经逐渐逼近Electron的114K。
它不仅吸引了许多Rust用户,还有很多前端非Rust用户尝试用它开发跨平台桌面应用程序。
可以预见Tauri 2.0之后会有越来越多的开发者用它开发跨平台移动应用APP。

我虽然也一直观望着Tauri的开发进展,但沉下心来仔细体验它还是头一次。今日借此机会,大致介绍了它的功能特点,初步总结了它的基础用法,希望对感兴趣的朋友们有帮助。

感觉TS/JS前端与Tauri后端的结合更妥帖,使用更方便,生态更好。Rust前端与Tauri后端之间交互较为麻烦,尚需继续打磨。

关于标题中提到的Sycamore相关体验,我会发布在后面的文章中,敬请期待。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/468385.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【统计子矩阵——部分前缀和+双指针】

题目 代码 #include <bits/stdc.h> using namespace std; typedef long long ll; const int N 510; int s[N][N]; int main() {ios::sync_with_stdio(0);cin.tie(0);int n, m, k;cin >> n >> m >> k;for(int i 1; i < n; i)for(int j 1; j <…

「QT」QT5程序设计专栏目录

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「QT」QT5程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasolid…

Qt学习笔记第41到50讲

第41讲 UI美化遗留问题解决 如上图所示目前记事本的雏形已现&#xff0c;但是还是有待优化&#xff0c;比如右下角的拖动问题。 解决方法&#xff1a; ①首先修改了Widget类的构造函数。 Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) {ui->s…

深度学习经典模型之ZFNet

1 ZFNet 1.1 模型介绍 ​ ZFNet是由 M a t t h e w Matthew Matthew D . Z e i l e r D. Zeiler D.Zeiler和 R o b Rob Rob F e r g u s Fergus Fergus在AlexNet基础上提出的大型卷积网络&#xff0c;在2013年ILSVRC图像分类竞赛中以11.19%的错误率获得冠军&#xff08;实际…

移动应用开发:简易登录页

文章目录 简介一&#xff0c;创建新活动二&#xff0c;设计UI布局三&#xff0c;编写活动代码四&#xff0c;运行应用程序注意 简介 使用Android Studio编写的简单Android 登录应用程序&#xff0c;该应用程序包含一个登录界面&#xff0c;具有账号和密码两个文本框&#xff0…

网络基础:http协议和内外网划分

声明 学习视频来自B站UP主泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 泷羽sec的个人空间-泷羽sec个人主页-哔哩哔哩视频https://space.bilibili.com/350329294 一&#xff0c;H…

英飞凌Aurix2G TC3XX GPT12模块详解

英飞凌Aurix2G TC3XX GPT12模块详解 本文主要介绍英飞凌 Aurix2G TC3XX系列芯片GPT12模块硬件原理、MCAL相关配置和部分代码实现。 文章目录 英飞凌Aurix2G TC3XX GPT12模块详解1 模块介绍2 功能介绍2.1 结构2.2 独立运行模式2.2.1 定时器模式2.2.2 门控定时器模式2.2.3 计数…

大数据程序猿不可不看的资料大全

​ 随着大数据技术的发展&#xff0c;大数据程序猿在数据采集、处理、分析、存储等方面的技能需求不断增加。要在这个领域保持竞争力&#xff0c;系统性地学习和掌握大数据工具、技术架构和行业趋势是非常重要的。以下为您提供一份围绕大数据程序猿不可不看的资料大全&#xf…

抓包工具WireShark使用记录

目录 网卡选择&#xff1a; 抓包流程&#xff1a; 捕获过滤器 常用捕获过滤器&#xff1a; 抓包数据的显示 显示过滤器&#xff1a; 常用的显示过滤器&#xff1a; 实际工作中&#xff0c;在平台对接&#xff0c;设备对接等常常需要调试接口&#xff0c;PostMan虽然可以进…

MySQL数据迁移到SQLServer数据库

随着云计算技术的发展以及大数据时代的到来&#xff0c;越来越多的企业开始寻求更加高效、安全的数据管理解决方案。MySQL作为一种开源的关系型数据库管理系统&#xff0c;在互联网应用开发中占据了极其重要的位置&#xff1b;而另一方面&#xff0c;Microsoft SQL Server凭借其…

【STM32开发】-FreeRTOS开发入手学习

一、什么是FreeRTOS&#xff1f; FreeRTOS 是 RTOS 系统的一种&#xff0c;FreeRTOS 十分的小巧&#xff0c;可以在资源有限的微控制器中运行&#xff1b; 1、 FreeRTOS是免费的。 2、许多其他半导体厂商产品的 SDK 包就使用 FreeRTOS 作为其操作系统&#xff0c;尤其是 WIFI、…

【软考】系统分析师第二版 新增章节 第20章微服务系统分析与设计

微服务系统是一类基于微服务架构风格的分布式系统&#xff0c;它将应用程序拆分成多个独立的小型服务&#xff0c;每个服务都运行在独立的进程中&#xff0c;并采用轻量级通信协议进行通信。这些服务可以由不同的团队开发、不同的编程语言编写&#xff0c;并且可以按需部署。微…

【笔记】自动驾驶预测与决策规划_Part6_不确定性感知的决策过程

文章目录 0. 前言1. 部分观测的马尔可夫决策过程1.1 POMDP的思想以及与MDP的联系1.1.1 MDP的过程回顾1.1.2 POMDP定义1.1.3 与MDP的联系及区别POMDP 视角MDP 视角决策次数对最优解的影响 1.2 POMDP的3种常规解法1.2.1 连续状态的“Belief MDP”方法1. 信念状态的定义2. Belief …

【SpringBoot】 黑马大事件笔记-day2

目录 用户部分 实体类属性的参数校验 更新用户密码 文章部分 规定josn日期输出格式 分组校验 上期回顾&#xff1a;【SpringBoot】 黑马大事件笔记-day1 用户部分 实体类属性的参数校验 对应的接口文档&#xff1a; 基本信息 请求路径&#xff1a;/user/update 请求方式&#…

HarmonyOS入门 : 获取网络数据,并渲染到界面上

1. 环境搭建 开发HarmonyOS需要安装DevEco Studio&#xff0c;下载地址 : https://developer.huawei.com/consumer/cn/deveco-studio/ 2. 如何入门 入门HarmonyOS我们可以从一个实际的小例子入手&#xff0c;比如获取网络数据&#xff0c;并将其渲染到界面上。 本文就是基于…

AndroidStudio-视图基础

一、设置视图的宽高 1.在XML文件中设置视图宽高 视图宽度通过属性android:layout_width表达&#xff0c;视图高度通过属性android:layout_height表达&#xff0c;宽高的取值主要有下列三种: &#xff08;1&#xff09;wrap_content:表示与内容自适应。对于文本视图来说&…

三菱QD77MS定位模块紧急停止功能

“紧急停止功能” 是通过简单运动模块的外部输入连接用连接器上连接的紧急停止输入&#xff0c;对同服放大器的全部轴进行批量停止的功能。(初始值为“0:有效”。)通过“[r.82]紧急停止有效/无效设置”可以选择紧急停止输入的有效/无效。 [1]控制内容 将“[r82]紧急停止有效/无…

Android JNI 技术入门指南

引言 在Android开发中&#xff0c;Java是一种主要的编程语言&#xff0c;然而&#xff0c;对于一些性能要求较高的场景&#xff08;如音视频处理、图像处理、计算密集型任务等&#xff09;&#xff0c;我们可能需要使用到C或C等语言来编写底层的高效代码。为了实现Java代码与C…

Js — 定时器

有两种&#xff1a;setInterval 和 setTimeout 间隔时间单位为毫秒 setInterval 每隔指定的毫秒数重复执行一个函数或代码 开启定时器&#xff1a;setInterval(函数&#xff0c;间隔时间) 作用&#xff1a;每隔一段时间调用这个函数 注意&#xff1a;它不是立即执行&#x…

H5播放器EasyPlayer.js 流媒体播放器是否支持npm(yarn) install 安装?

EasyPlayer.js H5播放器是一款功能强大的H5视频播放器&#xff0c;它支持多种流媒体协议播放&#xff0c;包括WebSocket-FLV、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WebRTC等格式的视频流。它不仅支持H.264和H.265编码格式&#xff0c;还具备实时录像、低延时直播等功能…