Tauri 是什么
Tauri 是一个跨平台 GUI
框架,与 Electron
的思维根本相似。Tauri 的前端实现也是基于 Web 系列语言,Tauri 的后端应用 Rust
。Tauri 能够创立体积更小、运行更快、更加平安的跨平台桌面利用。
为什么抉择 Rust?
Rust
是一门赋予每个人构建牢靠且高效软件能力的语言。它在高性能、可靠性、生产力方面体现尤为杰出。Rust 速度惊人且内存利用率极高,因为没有运行时和垃圾回收,它可能胜任对性能要求特地高的服务,能够在嵌入式设施上运行,还能轻松和其余语言集成。Rust 丰盛的类型零碎和所有权模型保障了内存平安和线程平安,让您在编译期就可能打消各种各样的谬误。Rust 也领有杰出的文档、敌对的编译器和清晰的谬误提示信息,还集成了一流的工具——包管理器和构建工具……
基于此,让 Rust 成为不二之选,开发人员能够很容易的应用 Rust 扩大 Tauri 默认的 Api
以实现定制化性能。
Tauri VS Electron
Detail | Tauri | Electron |
---|---|---|
Installer Size Linux | 3.1 MB | 52.1 MB |
Memory Consumption Linux | 180 MB | 462 MB |
Launch Time Linux | 0.39s | 0.80s |
Interface Service Provider | WRY | Chromium |
Backend Binding | Rust | Node.js (ECMAScript) |
Underlying Engine | Rust | V8 (C/C++) |
FLOSS | Yes | No |
Multithreading | Yes | Yes |
Bytecode Delivery | Yes | No |
Multiple Windows | Yes | Yes |
Auto Updater | Yes | Yes |
Custom App Icon | Yes | Yes |
Windows Binary | Yes | Yes |
MacOS Binary | Yes | Yes |
Linux Binary | Yes | Yes |
iOS Binary | Soon | No |
Android Binary | Soon | No |
Desktop Tray | Yes | Yes |
Sidecar Binaries | Yes | No |
环境装置
macOS
因为装置过程比较简单,作者应用的是 macOS,本文只介绍 macOS 装置步骤, Windows 装置步骤可自行查看官网。
1. 确保 Xcode 曾经装置
$ xcode-select --install
2. Node.js
倡议应用 nvm
进行 node 版本治理:
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
$ nvm install node --latest-npm$ nvm use node
强烈推荐装置 Yarn
,用来代替 npm。
3.Rust 环境
装置 rustup
:
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
验证 Rust
是否装置胜利:
$ rustc --versionrustc 1.58.1 (db9d1b20b 2022-01-20)
tips:如果 rustc
命令执行失败,能够重启一下终端。
至此,Tauri 开发环境已装置结束。
我的项目搭建
1.创立一个 Tauri 我的项目
$ yarn create tauri-app
按一下回车键,持续……
能够看出,目前支流的 Web 框架 Tauri 都反对,
咱们抉择 create-vite
……
此处抉择 Y
,将 @tauri-apps/api
装置进来,
而后抉择 vue-ts
……
查看 Tauri 相干的设置,确保所有就绪……
$ yarn tauri info
yarn run v1.22.17$ tauri infoOperating System - Mac OS, version 12.2.0 X64Node.js environment Node.js - 14.17.0 @tauri-apps/cli - 1.0.0-rc.2 @tauri-apps/api - 1.0.0-rc.0Global packages npm - 6.14.13 pnpm - Not installed yarn - 1.22.17Rust environment rustc - 1.58.1 cargo - 1.58.0Rust environment rustup - 1.24.3 rustc - 1.58.1 cargo - 1.58.0 toolchain - stable-x86_64-apple-darwinApp directory structure/dist/node_modules/public/src-tauri/.vscode/srcApp tauri.rs - 1.0.0-rc.1 build-type - bundle CSP - default-src 'self' distDir - ../dist devPath - http://localhost:3000/ framework - Vue.js✨ Done in 20.72s.
至此,一个新的 Tauri 我的项目已创立实现。
tips:Tauri 也反对基于已存在的前端我的项目进行集成,具体流程可查看官网,本文不做介绍。
我的项目目录介绍
├── README.md├── dist - web 我的项目打包编译目录│ ├── assets│ ├── favicon.ico│ └── index.html├── index.html ├── node_modules├── package.json├── public│ └── favicon.ico├── src - vue 我的项目目录(页面开发)│ ├── App.vue│ ├── assets│ ├── components│ ├── env.d.ts│ └── main.ts├── src-tauri - rust 相干目录(tauri-api 相干配置)│ ├── Cargo.lock│ ├── Cargo.toml - rust 配置文件│ ├── build.rs│ ├── icons - 利用相干的 icons│ ├── src - rust 入口│ ├── target - rust 编译目录│ └── tauri.conf.json - tauri 相干配置文件├── tsconfig.json├── tsconfig.node.json├── vite.config.ts└── yarn.lock
运行
运行我的项目:
$ cd tauri-demo1$ yarn tauri dev
期待我的项目 run 起来……
能够看到,一个基于 Vue 3 + TypeScript + Vite
的桌面端利用曾经运行起来了。
API 调用及性能配置
Tauri 的 Api 有 JavaScript Api
和 Rust Api
两种 ,本文次要抉择一些 Rust Api
来进行解说(Rust 相干常识可自行学习),JavaScript 相干的 Api 绝对简略一些,可依照官网文档进行学习。
1.Splashscreen(启动画面)
增加启动画面对于初始化耗时的利用来说是十分有必要的,能够晋升用户体验。
大抵原理是在利用初始化阶段先暗藏主利用视图,展现启动画面视图,期待初始化实现当前动静敞开启动画面视图,展现主视图。
首先在我的项目根目录创立一个 splashscreen.html
文件作为启动画面视图,具体展现内容可自行配置,代码如下:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Loading</title></head><body style="background-color: aquamarine;"> <h1>Loading...</h1></body></html>
其次更改 tauri.conf.json
配置项:
"windows": [ { "title": "Tauri App", "width": 800, "height": 600, "resizable": true, "fullscreen": false,+ "visible": false // 默认暗藏主视图 }, // 增加启动视图+ {+ "width": 400,+ "height": 200,+ "decorations": false,+ "url": "splashscreen.html",+ "label": "splashscreen"+ }]
将 windows
配置项下的主视图 visible
属性设置为 false
,这样初始化阶段,主视图就会暗藏;
在 windows
配置项下新建一个启动视图,视图大小能够自定义配置。
接下来就是动态控制两个视图的显示和暗藏了。
关上 src-tauri/main.rs
文件,增加以下 Rust 代码:
use tauri::Manager;// 创立一个 Rust 命令#[tauri::command]fn close_splashscreen(window: tauri::Window) { // 敞开启动视图 if let Some(splashscreen) = window.get_window("splashscreen") { splashscreen.close().unwrap(); } // 展现主视图 window.get_window("main").unwrap().show().unwrap();}fn main() { tauri::Builder::default() // 注册命令 .invoke_handler(tauri::generate_handler![close_splashscreen]) .run(tauri::generate_context!()) .expect("error while running tauri application");}
以上 Rust 代码的执行逻辑是创立一个 close_splashscreen
函数用来敞开启动视图并展现主视图,并将这个函数注册为一个 Rust 命令,在利用初始化时进行注册,以便在 JavaScript 中能够动静调用该命令。
接下来,在 src/App.vue
中增加以下代码:
// 导入 invoke 办法import { invoke } from '@tauri-apps/api/tauri'// 增加监听函数,监听 DOM 内容加载实现事件document.addEventListener('DOMContentLoaded', () => { // DOM 内容加载实现之后,通过 invoke 调用 在 Rust 中曾经注册的命令 invoke('close_splashscreen')})
咱们能够看一下 invoke
办法的源码:
/** * Sends a message to the backend. * * @param cmd The command name. * @param args The optional arguments to pass to the command. * @return A promise resolving or rejecting to the backend response. */async function invoke<T>(cmd: string, args: InvokeArgs = {}): Promise<T> { return new Promise((resolve, reject) => { const callback = transformCallback((e) => { resolve(e) Reflect.deleteProperty(window, error) }, true) const error = transformCallback((e) => { reject(e) Reflect.deleteProperty(window, callback) }, true) window.rpc.notify(cmd, { __invokeKey: __TAURI_INVOKE_KEY__, callback, error, ...args }) })}
invoke
办法是用来和后端(Rust)进行通信,第一个参数 cmd
就是在 Rust 中定义的命令,第二个参数 args
是可选的配合第一个参数的额定信息。办法外部通过 window.rpc.notify
来进行通信,返回值是一个 Promise。
至此,增加启动视图的相干逻辑已全副实现,咱们能够运行查看一下成果。
因为咱们的 demo 我的项目初始化很快,不容易看到启动视图,因而可通过 setTimeout
提早 invoke('close_splashscreen')
的执行,不便调试查看:
能够看到,在我的项目运行起来之后,首先展现的是启动视图,其次启动视图隐没,主视图展现进去。
2.Window Menu(利用菜单)
为利用增加菜单是很根底的性能,同时也很重要。
关上 src-tauri/main.rs
文件,增加以下 Rust 代码:
use tauri::{ Menu, Submenu, MenuItem, CustomMenuItem };fn main() { let submenu_gear = Submenu::new( "Gear", Menu::new() .add_native_item(MenuItem::Copy) .add_native_item(MenuItem::Paste) .add_native_item(MenuItem::Separator) .add_native_item(MenuItem::Zoom) .add_native_item(MenuItem::Separator) .add_native_item(MenuItem::Hide) .add_native_item(MenuItem::CloseWindow) .add_native_item(MenuItem::Quit), ); let close = CustomMenuItem::new("close".to_string(), "Close"); let quit = CustomMenuItem::new("quit".to_string(), "Quit"); let submenu_customer = Submenu::new( "Customer", Menu::new() .add_item(close) .add_item(quit) ); let menus = Menu::new() .add_submenu(submenu_gear) .add_submenu(submenu_customer); tauri::Builder::default() // 增加菜单 .menu(menus) // 监听自定义菜单事件 .on_menu_event(|event| match event.menu_item_id() { "quit" => { std::process::exit(0); } "close" => { event.window().close().unwrap(); } _ => {} }) // 注册命令 .invoke_handler(tauri::generate_handler![close_splashscreen]) .run(tauri::generate_context!()) .expect("error while running tauri application");}
首先咱们引入 Menu
、Submenu
、MenuItem
、CustomMenuItem
。
查看 Menu
以及 Submenu
源码:
/// A window menu.#[derive(Debug, Clone)]#[non_exhaustive]pub struct Menu { pub items: Vec<MenuEntry>,}impl Default for Menu { fn default() -> Self { Self { items: Vec::new() } }}#[derive(Debug, Clone)]#[non_exhaustive]pub struct Submenu { pub title: String, pub enabled: bool, pub inner: Menu,}impl Submenu { /// Creates a new submenu with the given title and menu items. pub fn new<S: Into<String>>(title: S, menu: Menu) -> Self { Self { title: title.into(), enabled: true, inner: menu, } }}impl Menu { /// Creates a new window menu. pub fn new() -> Self { Default::default() } /// Adds the custom menu item to the menu. pub fn add_item(mut self, item: CustomMenuItem) -> Self { self.items.push(MenuEntry::CustomItem(item)); self } /// Adds a native item to the menu. pub fn add_native_item(mut self, item: MenuItem) -> Self { self.items.push(MenuEntry::NativeItem(item)); self } /// Adds an entry with submenu. pub fn add_submenu(mut self, submenu: Submenu) -> Self { self.items.push(MenuEntry::Submenu(submenu)); self }}
Menu
这个构造体就是用来实现利用菜单的,它内置的 new
关联函数用来创立 menu
,add_item
办法用来增加自定义菜单项,add_native_item
办法用来增加 Tauri 原生实现的菜单项,add_submenu
用来增加菜单入口。
Submenu
这个构造体用来创立菜单项的入口。
如图:
箭头所指的 Gear
和 Customer
就是 Submenu
,红框里是 Submenu
下所蕴含的 MenuItem
项。
咱们创立了一个命名为 Gear
的 Submenu
,并增加了一些 Tauri 原生反对的 MenuItem
项进去。
咱们也创立了一个命名为 Customer
的 Submenu
,并增加了两个自定义的 CustomMenuItem
项,CustomMenuItem
的事件须要开发者本人定义:
// 监听自定义菜单事件on_menu_event(|event| match event.menu_item_id() { "quit" => { // 逻辑自定义 std::process::exit(0); } "close" => { // 逻辑自定义 event.window().close().unwrap(); } _ => {}})
通过 on_menu_event
办法监听自定义菜单项的触发事件,它接管的参数是一个 闭包
,用 match
对菜单项的 事件 id
进行匹配,并增加自定义逻辑。
注意事项
Tauri 原生反对的 MenuItem 菜单项存在兼容性问题,能够看源码:
/// A menu item, bound to a pre-defined action or `Custom` emit an event. Note that status bar only/// supports `Custom` menu item variants. And on the menu bar, some platforms might not support some/// of the variants. Unsupported variant will be no-op on such platform.#[derive(Debug, Clone)]#[non_exhaustive]pub enum MenuItem { /// A menu item for enabling cutting (often text) from responders. /// /// ## Platform-specific /// /// - **Windows / Android / iOS:** Unsupported /// Cut, /// A menu item for pasting (often text) into responders. /// /// ## Platform-specific /// /// - **Windows / Android / iOS:** Unsupported /// Paste, /// Represents a Separator /// /// ## Platform-specific /// /// - **Windows / Android / iOS:** Unsupported /// Separator, ...}
能够看出内置的这些菜单项在 Windows
、Android
、iOS
平台都还不反对,然而随着稳定版的公布,置信这些兼容性问题应该能失去很好的解决。
调试
在开发模式下,调试绝对容易。以下来看在开发模式下如何别离调试 Rust
和 JavaScript
代码。
Rust Console
调试 Rust 代码,咱们能够应用 println!
宏,来进行调试信息打印:
let msg = String::from("Debug Infos.")println!("Hello Tauri! {}", msg);
调试信息会在终端打印进去:
WebView JS Console
JavaScript 代码的调试,咱们可应用 console
相干的函数来进行。在利用窗口右键单击,抉择 Inspect Element
即 审查元素,就能够关上 WebView 控制台。
控制台相干的操作就不再赘述了。
tips:在一些状况下,咱们可能也须要在最终包查看 WebView 控制台,因而 Tauri 提供了一个简略的命令用来创立 调试包
:
yarn tauri build --debug
通过该命令打包的应用程序将搁置在 src-tauri/target/debug/bundle
目录下。
利用打包
yarn tauri build
该命令会将 Web 资源 与 Rust 代码一起嵌入到单个二进制文件中。二进制文件自身将位于 src-tauri/target/release/[app name]
,安装程序将位于 src-tauri/target/release/bundle/
。
Roadmap
从 Tauri 的 Roadmap
能够看出,稳定版会在 2022 Q1
公布,包含后续对 Deno
的反对,以及打包到挪动设施的反对。因而 Tauri 的倒退还是很值得期待的。
总结
Tauri 主打的 更小、更快、更平安,相较于 Electron
让人诟病的包太大、内存耗费过大等问题来看,确实是一个很有后劲的桌面端利用开发框架,同时在 Rust
的加持下如有神助,让这款桌面端利用开发框架极具魅力。不过因为 Tauri 到目前为止还没公布稳定版,以及一些性能还存在多平台兼容性等问题,以致目前还不能在生产环境进行大面积利用。置信随着 Tauri 的倒退,这些问题都会失去解决,当前的桌面端利用开发市场中也会有很大一部分份额会被 Tauri 所占有。作为开发者的咱们,此刻正是学习 Tauri
以及 Rust
的最佳时机,口头起来吧~