Rust によるデスクトップアプリケーションフレームワーク Tauri 2022-03-06 | 11 min read Author: masahiro-kondo Follow @MamezouDev Tweet シェア #Tauri Tauri はクロスプラットフォームデスクトップアプリのための Rust によるフレームワークです。Electron と同様 Web 技術でアプリの UI を構築します。 Build smaller, faster, and more secure desktop applications with a web frontend | Tauri Studio 記事執筆時点で v1.0.0-rc.5 (Pre-release)、もうすぐ v1.0 がリリースされそうなところです。 Release v1.0.0-rc.5: Apply Version Updates From Current Changes (#3468) · tauri-apps/tauri Electron と比べて、インストーラーのサイズ、メモリ消費量、起動時間などが圧倒的に小さいと謳われています。 https://github.com/tauri-apps/tauri#comparison-between-tauri-and-electron Electron では main プロセスを Node.js、UI を Chrome という構成でアプリケーションを実行します。Tauiri は Electron の main プロセスに相当する部分を Rust、UI は各 OS 標準の WebView でレンダリングする wry というライブラリを使用します。 GitHub - tauri-apps/wry: Cross-platform WebView library in Rust for Tauri. macOS では、WebKit、Windows では、WebViwe2[1]、Linux では WebKitGTK を使用します。Electron と違ってクロスブラウザ的な動作の違いはありそうですが、この方式でモバイル対応も計画されています。 簡単なアプリケーションを作って開発体験を見てみます。 Information記事執筆時点では M1 Mac ではビルドがうまくできず、Intel Mac と Windows 10 で確認しました。 まず Rust を最新版に更新します。[2] rustup update Node.js も最新安定版[3]をインストールしておきます。 環境が整ったら、アプリケーションのボイラープレートを生成します。選択肢はすべてデフォルトを指定しました。 $ npx create-tauri-app Need to install the following packages: create-tauri-app Ok to proceed? (y) Press any key to continue... ? What is your app name? tauri-app ? What should the window title be? Tauri App ? What UI recipe would you like to add? Vanilla.js (html, css, and js without the bundlers) ? Add "@tauri-apps/api" npm package? Yes >> Running initial command(s) >> Installing any additional needed dependencies - Installing @tauri-apps/cli@latest... added 2 packages, and audited 3 packages in 4s 1 package is looking for funding run `npm fund` for details found 0 vulnerabilities - Installing @tauri-apps/api@latest... added 2 packages, and audited 5 packages in 3s 3 packages are looking for funding run `npm fund` for details found 0 vulnerabilities >> Updating "package.json" >> Running "tauri init" > tauri > tauri "init" "--app-name" "tauri-app" "--window-title" "Tauri App" "--dist-dir" "../dist" "--dev-path" "../dist" "--ci" >> Updating "tauri.conf.json" >> Running final command(s) Your installation completed. $ cd tauri-app $ npm install $ npm run tauri dev 生成されたプロジェクトの構造は以下のようになっています。dist 配下に Web ページ、src-tauri 配下にソースコードやアイコンなどが格納されています。 ├── dist │ └── index.html ├── node_modules ├── package-lock.json ├── package.json └── src-tauri ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── icons │ ├── 128x128.png │ ├── [email protected] │ ├── 32x32.png │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ ├── Square30x30Logo.png │ ├── Square310x310Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── StoreLogo.png │ ├── icon.icns │ ├── icon.ico │ └── icon.png ├── src │ └── main.rs └── tauri.conf.json package.json はシンプルです。 { "name": "tauri-app", "scripts": { "tauri": "tauri" }, "devDependencies": { "@tauri-apps/cli": "^1.0.0-rc.5" }, "dependencies": { "@tauri-apps/api": "^1.0.0-rc.1" } } tauri という CLI により、アプリケーション起動やリリースビルドなど行います。tauri.conf.json に記述されているアプリ構成が実行されます。tauri dev を実行すると Rust のパッケージマネージャが必要なパッケージをダウンロードしビルドします。最後にアプリケーションが DevTools 利用可能な状態で起動します。 $ npm run tauri dev > tauri > tauri "dev" Compiling libc v0.2.119 Compiling cfg-if v1.0.0 Compiling proc-macro2 v1.0.36 : Compiling tauri-utils v1.0.0-rc.2 Compiling tauri-runtime v0.3.2 Compiling tauri-codegen v1.0.0-rc.2 Compiling tauri-build v1.0.0-rc.3 Compiling tauri-runtime-wry v0.3.2 Compiling tauri-macros v1.0.0-rc.2 Compiling app v0.1.0 (/Users/masa/codes/rust-study/tauri-app/src-tauri) Finished dev [unoptimized + debuginfo] target(s) in 3m 31s Running `target/debug/app` 起動したアプリの画面上で右クリックすると Reload や DevTools の起動が可能です。 Windows でも同様です。 初回はパッケージダウンロードとビルドで時間がかかりますが、ビルド結果は、src-tauri/target にキャッシュされるため2回目以降はすぐに起動します。 公式ドキュメントには HTML/CSS/TS/JS など UI 部分のコードを変更すると devserver が instant hot module reloading で即時反映するとあります。Vanilla.js だとホットリロードは効きませんでした。試しに create-tauri-app で Svelte を選択すると、Svelte 公式のアプリ生成コマンドにより Svelte アプリの雛型が作成されました。そして tauri-svelte というモジュールが rollup で Svelte のコードをバンドル、Tauri アプリ内で Svelte アプリをホストしてホットリロードが可能になりました。 $ npm run tauri dev > [email protected] tauri > tauri "dev" [tauri:dev] Running `npm run dev` Finished dev [unoptimized + debuginfo] target(s) in 0.26s Running `target/debug/app` > [email protected] dev > rollup -c -w rollup v2.69.1 bundles src/main.ts → public/build/bundle.js... LiveReload enabled on port 35730 created public/build/bundle.js in 1.7s [2022-03-06 10:23:20] waiting for changes... > [email protected] start > sirv public --no-clear "--dev" Your application is ready~! 🚀 - Local: http://localhost:8080 - Network: Add `--host` to expose ────────────────── LOGS ────────────────── Rust のコードを変更した場合はリビルドとリロードが自動的に行われました。 Informationcreate-tauri-app では、メジャーなフロントエンドのフレームワークの生成ツールと連携してプロジェクトを生成できます。プレーンな Vanilla.js 以外に、以下の生成ツールを選択可能です。 create-react-app create-vite Vue CLI Angular CLI Svelte Solid Dominator ClojureScript tauri build を実行すると実行したプラットフォーム用のインストーラーが生成されます。 npm run tauri build 生成された macOS 用の dmg ファイル、Windows 用の MSI ファイルは共に 4MB 弱でした。確かに Electron の 100MB 弱と比べると格段に小さいですね。 公式のサンプルアプリ集が tauri リポジトリにあります。 tauri/examples at dev · tauri-apps/tauri state サンプルのカウンターの UI を切り出して、create-tauri-app で生成されたプロジェクトに移植してみました[4]。 HTML/JS から Tauri の機能を利用するため、tauri.conf.json の build セクションに "withGlobalTauri": true を追加します。これで JS から window.__TAURI__ というプロパティ経由で tauri の API を利用できるようになります。 "build": { "distDir": "../dist", "devPath": "../dist", "beforeDevCommand": "", "beforeBuildCommand": "", "withGlobalTauri": true }, dist/index.html を以下のコードで置き換え。カウントアップ用のボタンと値を表示するだけの UI です。ボタンのクリックイベントで window.__TAURI__ オブジェクトを使って Rust 側の処理を invoke し、結果を表示しています。 <!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>Tauri</title> </head> <body> <h3>Counter</h3> <div> <button id="increment-btn">Increment counter</button> </div> <div id="response"></div> <script> const KEY = 'db-key' const incrementBtn = document.querySelector('#increment-btn') const responseContainer = document.querySelector('#response') function updateResponse(response) { responseContainer.innerText = typeof response === 'string' ? response : JSON.stringify(response) } incrementBtn.addEventListener('click', () => { window.__TAURI__ .invoke('increment_counter') .then(updateResponse) .catch(updateResponse) }) </script> </body> </html> src/main.rs を以下のコードで置き換えます。tauri::command のアトリビュートが付与された関数群を定義しています。UI から invoke される increment_counter 以外に UI との通信設定が実装されています。 #![cfg_attr( all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows" )] use std::{ sync::{ atomic::{AtomicUsize, Ordering}, Mutex, }, }; use tauri::State; struct Counter(AtomicUsize); struct Client; impl Client { fn send(&self) {} } #[derive(Default)] struct Connection(Mutex<Option<Client>>); #[tauri::command] fn connect(connection: State<'_, Connection>) { *connection.0.lock().unwrap() = Some(Client {}); } #[tauri::command] fn disconnect(connection: State<'_, Connection>) { // drop the connection *connection.0.lock().unwrap() = None; } #[tauri::command] fn connection_send(connection: State<'_, Connection>) { connection .0 .lock() .unwrap() .as_ref() .expect("connection not initialize; use the `connect` command first") .send(); } #[tauri::command] fn increment_counter(counter: State<'_, Counter>) -> usize { counter.0.fetch_add(1, Ordering::Relaxed) + 1 } fn main() { tauri::Builder::default() .manage(Counter(AtomicUsize::new(0))) .manage(Connection(Default::default())) .invoke_handler(tauri::generate_handler![ increment_counter, connect, disconnect, connection_send ]) .run(tauri::generate_context!( "tauri.conf.json" )) .expect("error while running tauri application"); } アプリを起動し increment counter ボタンをクリックすると、テキストフィールドの数値がインクリメントされます。 Tauri ではいくつかのアプリケーション構成のパターンが定義されています。ドキュメントにわかりやすいダイアグラムと共に解説があります。 Hermit Bridge Cloudish Cloudbridge Lockdown Multiwin GLUI 上記のカウンターの例は、Lockdown パターンのようです。 flowchart LR subgraph Rust Binary BS{Bootstrap} API[API:Event] end subgraph WebView P((Promise Closure)) Window end Binary-->BS Binary-->API BS-->Window API-->P P-.->API P-->WindowBridge パターンは Lockdown のスーパーセットで Rust と WebView 双方にある Broker オブジェクト経由で通信します。 Tauri は Electron よりも Web フレームワークの利用が簡単です。開発体験としては、Rust のビルドに時間がかかりますが、キャッシュされてしまえば Electron での開発と同じような感覚で UI 周りの実装はできる感じでした。今のところ Electron のようにリモートのサイトをまるっとアプリのコンテンツとして表示する機能はなさそうです。 訂正 (2022.07.06)tauri.conf.json の build/devPath や build/distDir に URL を指定するとリモートのサイトをコンテンツとしてロード可能でした。 https://tauri.app/v1/api/config/#buildconfig Electron からの移行に際してハードルになるのは、やはり Rust しょう。Electron アプリは JavaScript だけで書けますが、Tauri を使いこなすには Rust の知識が不可欠です。厳密な型付けやメモリ安全性を備える Rust を使うことで堅牢なアプリケーションが作成できそうです。Rust に CPU パワーが要るのでなるべく性能の高いマシンが欲しいところです。 いずれにしても、Electron を凌駕する可能性のあるフレームワークということは言えそうです。 Microsoft Edge (Chromium) のエンジン。 ↩︎ rustup は各 OS 用にインストーラが用意されているので事前にインストールしておきます。macOS では Xcode command line tools, Windows では Visual Studio で C++ のビルド環境を構築しておく必要があります。 ↩︎ 記事執筆時点では、16.14.0 LTS でした。 ↩︎ examples は tauri 自体をビルドして、tauri プロジェクト配下でビルドする構成のため、単独のプロジェクトとしては動作しません。 ↩︎ 豆蔵デベロッパーサイト - 先週のアクセスランキング【新人さん向け】A5:SQL Mk-2のおすすめ設定と使い方(2024-06-08)基本から理解するJWTとJWT認証の仕組み(2022-12-08)2024年版!VS Code で Java 開発環境を構築する(2024-07-18)文字コード これだけは覚えておこう ~シフトJIS編~(2024-06-16)GitHub Issues で Sub-issues が作れるようになりました(ベータ)(2024-10-05)【devcontainer完全ガイド】DockerとWSLで最強のイマドキ開発環境を手に入れよう!(2024-10-11)Google Test を使ってみる(その5:GoogleMock編)(2023-10-08)手書き風ドローツール Excalidraw のススメ(2023-03-22)IoT を使ってみる(その15:ESP32のディープスリープで長時間バッテリー駆動に挑戦)(2024-02-24)統計学で避けて通れない自由度の話(2022-06-20) 豆蔵では共に高め合う仲間を募集しています!具体的な採用情報はこちらからご覧いただけます。