概要
最近趣味でRustをやっています。
そのなかで、WASM(WebAssembly)の技術を使ってRustでフロントエンド開発が行えると知ったので少し試しました。
今回はYewというフレームワークのチュートリアルを参考に、環境の立ち上げから簡単なカウンターボタンを表示するところまでやってみます。
執筆者について
普段はGoでバックエンド開発をしています。
フロントエンドはたまにAngularをやる程度であまり詳しくありません。
むしろ、昔ながらのPHPのテンプレートエンジンとjQueryをつかった開発の方が長いです。
Rustについても経験浅なので不正確な記述などあればご指摘ください。
なぜブラウザでRustが動かせるのか
WebAssemblyのおかげ
https://webassembly.org/
当初はWebブラウザ上でJavaScriptで行うには負荷が高い処理を高速で実行させるために開発された技術ですが、最近ではブラウザ上以外でも利用できるようになっています。
RustをWebAssembly形式にコンパイルすることで、ブラウザ上でRustで書いたコードが動かせます。
Rust以外にも、C, C++やGoもWebAssemblyにコンパイルすることができます。
デザインツールとして有名なFigmaはWebAssemblyを使うことで処理速度を格段に上げることに成功したみたいです。
https://www.figma.com/blog/webassembly-cut-figmas-load-time-by-3x/
Yew
Rustを単にWebAssemblyにコンパイルするためには特にフレームワークを利用する必要はありません。しかし今回はチュートリアルが充実している、活発に開発されているという理由でYewというフレームワークを試してみます。
Rustでフロントエンド開発を行うフレームワークは各種ありますが、ダウンロード数などはYewが圧倒的です。
※参考
https://github.com/flosse/rust-web-framework-comparison#frontend-frameworks-wasm
この中だったら次はeguiやicedを試してみたいかも
やってみよう
セットアップ
trunk
まずはtrunkというビルドツールをインストールします。
https://trunkrs.dev/
trunkはRustでWebAssemblyアプリケーションを作るときに利用するビルドツールです。ローカルサーバーもついてて便利、ホットリロードもしてくれます。Yewではこれを使うのが基本のよう
cargo install trunk
WebAssemblyコンパイルのためコンパイルターゲットアーキテクチャ追加
rustup target add wasm32-unknown-unknown
これでRustがWeb Assemblyをコンパイルできるようになります。
ちなみにこの変な名前は
https://github.com/rustwasm/wasm-bindgen/issues/979#issuecomment-432632829
My understanding is that the first unknown is the system that you are compiling on, and the second is the system you are targeting.
So you an think of unknown-unknown as
“Compile on almost any machine, run on almost any machine”
If you look at other targets “unknown” is commonly used so this is in line with the other targets.
Hope that helps!!
ChatGPTによる日本語翻訳
私の理解によれば、最初のunknownはコンパイルしているシステムであり、2つ目はターゲットとしているシステムです。
したがって、unknown-unknownは
「ほぼどんなマシン上でもコンパイルし、ほぼどんなマシン上でも実行する」
と考えることができます。
他のターゲットを見ると、「unknown」はよく使われているので、これは他のターゲットと一致しています。
お手伝いできれば幸いです!!
とのことです。
全然関係ないですが、最近読んだ本にソフトウェアを複雑さを表す3つの概念の中の一つにunknown unknownsというものがありました。
https://www.amazon.co.jp/-/en/John-Ousterhout/dp/1732102201
変更などを加えるためにどこに依存関係などがあるか全くわからず手をつける場所が不明になってしまうことらしいです。
よくみるやつですね。
プロジェクト作成
cargo new yew-app
このコマンドを打つことでプロジェクトのディレクトリが作成されます。
cargo.tomlというRustプロジェクトの設定ファイルに以下を記述し、Yewを使えるようにします。
今回はクライアントサイドレンダリングを設定しますが、サーバーサイドレンダリングもできるみたいです。
https://docs.rs/yew/latest/yew/
[dependencies]
yew = { version = "0.20", features = ["csr"] }
あとはこんなふうに記述して
use yew::prelude::*;
#[function_component(App)]
fn app() -> Html {
html! {
<h1>{ "Hello World" }</h1>
}
}
fn main() {
yew::Renderer::<App>::new().render();
}
プロジェクトルートにhtmlおいて
<!DOCTYPE html>
<html lang="en">
<head></head>
<body></body>
</html>
このコマンドでローカルサーバーが立ち上がります。
trunk serve --open
スクリーン録画したら遅くなってしまいましたが、実際はホットリロードもっとはやいです。
カウンターをつくる
use yew::prelude::*;
#[function_component(App)]
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();
}
すごーい!
yew::functional::use_state
Yewでstate管理をするときに使われる関数のようです。
Rustの記法が分かりづらいけど、初期化のときに関数を受け取っています。ここでは初期値を0にしています。
// || 0 が初期値を入れる関数
let counter = use_state(|| 0);
// これはライブラリ内の定義です
pub fn use_state<T, F>(init_fn: F) -> UseStateHandle<T>
onclick
onclickが押されるとlet onclickが動きます。onclickは関数を返し、move |_| 内部の処理が動きます。これはクロージャーで、moveはcounterの所有権をもった関数を返すための魔法です。
let onclick = {
let counter = counter.clone();
move |_| {
let value = *counter +1;
counter.set(value);
}
};
html! {
<div>
<button {onclick}>{"+1"}</button>
<p>{*counter}</p>
</div>
}
コンパイラが親切
上記onclickからmoveを消してみました。
let onclick = {
let counter = counter.clone();
|_| {
let value = *counter +1;
counter.set(value);
}
};
html! {
<div>
<button {onclick}>{"+1"}</button>
<p>{*counter}</p>
</div>
}
すると以下のようなコンパイルエラーが出て、moveしないとダメだからつけてみてねと言ってくれます。
感想
ブラウザ上でJavaScript以外の言語が動かせるなんてちょっと感動。
ただ今回やったようなことはRustみたいな言語を使う必要はなさそう(むしろVueやReact使う方が簡単)
今後もうちょっと詳しくtodoアプリみたいなチュートリアルもやろうかと思うけど、そのようなものをRustで作る必要があるかも微妙な気がする。
ブラウザ上のWebAssembly自体は、多分こう言うものよりもグラフィックや重い処理をやらせるために使うのが本来の目的なんだろうと思います。
Rustのコンパイラの親切さはYewを使っていても健在でした。
そして今日みたこの本が欲しくなりました。
https://amzn.asia/d/fjTtVmg