Tauri 2.0で使えるRust UIテンプレートの比較
この記事はRust Advent Calendar 2024シリーズ2の9日目の記事です。
Rustでデスクトップ/モバイルアプリを製作するソリューションの一つとしてTauriがあります。
10月には、2.0がリリースされました🎉
そんなTauriでUIを実装する際には、選択肢として、次の3つがあります。
- Rust
- TypeScript / JavaScript
- .NET
せっかくなので、UIもRustを使いたいですよね。となると、現状では4つのテンプレートから選択することになります。
- Vanilla
- Yew
- Leptos
- Sycamore
Vanillaは「自分でなんとかします」という意味なので、そうでなければ、他3つのいずれかを使うことになります。
しかし、この3つの違いがよく分からなかったため、少し調べてみました。
データで比較
まずはGitHubから分かるデータの比較です。
名称 | 最新版 | 最初のコミット | スター数(GitHub) | コントリビューター数 |
---|---|---|---|---|
Yew (ユー) | 0.21.0 (2023/9/29) | 2017/12/16 | 30,000+ | 400+ |
Leptos (レプトス) | 0.7.0 (2024/12/1) | 2022/8/1 | 16,000+ | 300+ |
Sycamore (シカモア) | 0.9.1 (2024/11/18) | 2021/3/6 | 2,000+ | 50+ |
Yewは最も歴史が古く、スター数も多いですが、近年は更新が少なめです。
Leptosは新しいながらもすでに16,000以上のスターを獲得しており、最も勢いがあります。
Sycamoreも精力的に更新が続けられています。
各フレームワークのサンプルを見てみましょう。
Yewのサンプル
Build a sample appにあったカウンターの実装を見てみます。
use yew::prelude::*;
#[function_component]
fn App() -> Html {
let counter = use_state(|| 0);
let onclick = {
let counter = counter.clone();
move |_| {
let value = *counter + 1;
counter.set(value);
}
};
html! {
<div>
<button {onclick}>{ "+1" }</button>
<p>{ *counter }</p>
</div>
}
}
fn main() {
yew::Renderer::<App>::new().render();
}
関数に対し functional_component
マクロを適用することでコンポーネントを生成するようです。
コンポーネントの状態は use_state
関数で、ボタンを押したときの処理もクロージャーを使って書けます。
UIは html!
マクロの中でJSXに似た構文で書けば良さそうです。
なお、構造体コンポーネントとしての実装もできるようです。
Leptosのサンプル
A Basic Componentにあったカウンターの実装を見てみます。
use leptos::*;
#[component]
fn App() -> impl IntoView {
let (count, set_count) = create_signal(0);
view! {
<button
on:click=move |_| {
// on stable, this is set_count.set(3);
set_count(3);
}
>
"Click me: "
// on stable, this is move || count.get();
{move || count()}
</button>
}
}
fn main() {
leptos::mount_to_body(|| view! { <App/> })
}
こちらも、 component
マクロを適用することでコンポーネントを生成するようです。
コンポーネントの状態は create_signal
関数で、ボタンを押したときの処理もクロージャーを使って書けます。
UIは view!
マクロの中でJSXに似た構文で書けば良さそうです。
Builderパターンを使ったUIの構築もできるようです。
Sycamoreのサンプル
examplesにあったカウンターの実装を見てみます。
use sycamore::prelude::*;
#[component]
fn App() -> View {
let mut state = create_signal(0i32);
let increment = move |_| state += 1;
let decrement = move |_| state -= 1;
let reset = move |_| state.set(0);
view! {
div {
p { "Value: " (state) }
button(on:click=increment) { "+" }
button(on:click=decrement) { "-" }
button(on:click=reset) { "Reset" }
}
}
}
fn main() {
sycamore::render(App);
}
こちらも、 component
マクロを適用することでコンポーネントを生成するようです。
コンポーネントの状態は create_signal
関数で、ボタンを押したときの処理もクロージャーを使って書けます。
UIは view!
マクロの中で、独自のDSLを使って書くようです。
Builderパターンを使ったUIの構築もできるようです。
結局のところ、何を使えばいいのか
3つのテンプレートを見てみましたが、単純な開発体験だけであれば、ほとんど差はなさそうに見えます。
なので、現状ではフィーリングで決めてしまっても問題ないと思います。
とはいえ、全く同じというわけでもないため、簡単に使い分けを考えてみたいと思います。
仮想DOM vs. きめ細かな反応性
インスパイアされたフレームワークの違いから、データ変化が発生した際のUIの更新ポリシーが異なります。
- 仮想DOM (Virtual DOM)
- データ変化を仮想DOM(メモリ上にコピーしたオブジェクト)に適用し、実DOMに差分を適用する
- きめ細かな反応性 (Fine-Grained Reactivity)
- 「データが変わったときに実DOMのどこを変更すべきか」を管理し、データの変更に対して直接DOMを書き換える
仮想DOMの方は、実DOMとの差分検出に一定処理時間がかかるため、きめ細かな反応性の方が表示速度上では有利です。
それぞれの思想は下記のとおりです。
名称 | インスパイアされたフレームワーク | ビュー更新設計 |
---|---|---|
Yew | React | 仮想DOM |
Leptos | SolidJS | きめ細かな反応性 |
Sycamore | SolidJS | きめ細かな反応性 |
The Elm Architecture
アーキテクチャ設計として、Elm言語で用いられているThe Elm Architecture (TEA)というものが存在します。
YewはElmにもインスパイアされており、構造体コンポーネントではTEAを使用することができます。
JSX vs. 独自DSL
YewとLeptosは、基本的には <>
を使ったJSXに似た構文で書けます。
一方、Sycamoreは ()
や {}
を使った独自DSLを使って書きます。
今回は取り上げませんでしたが、Dioxusというフルスタックフレームワークもこれとよく似たDSLを採用しています。
まとめ
どれを使っても良いですが、迷ったら下記のように考えてみると良いかもしれません。
- ReactかElmのように書きたい、または安定しているものを使いたい
- Yew
- メンテナンスが活発なものを使いたい、または表示速度にこだわりたい
- Solid JSのように書きたい、またはそこそこ大きなコミュニティのあるものを使いたい
- Leptos
-
div (class="container") { "Hello, world!" }
という書き方が直感的に感じられた- Sycamore
- Solid JSのように書きたい、またはそこそこ大きなコミュニティのあるものを使いたい
Discussion